mod.rs 28.1 KB
Newer Older
1 2 3 4 5 6 7 8 9 10
// Copyright 2012 The Rust Project Developers. See the COPYRIGHT
// 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

P
Patrick Walton 已提交
13

N
Niko Matsakis 已提交
14
use mc = middle::mem_categorization;
15
use middle::ty;
16
use middle::typeck;
17
use middle::moves;
N
Niko Matsakis 已提交
18 19
use middle::dataflow::DataFlowContext;
use middle::dataflow::DataFlowOperator;
A
Alex Crichton 已提交
20
use util::common::stmt_set;
21
use util::ppaux::{note_and_explain_region, Repr, UserString};
22

23 24 25 26
use std::hashmap::{HashSet, HashMap};
use std::io;
use std::ops::{BitOr, BitAnd};
use std::result::{Result};
27
use syntax::ast;
N
Niko Matsakis 已提交
28
use syntax::ast_map;
29
use syntax::codemap::Span;
30
use syntax::parse::token;
31 32 33
use syntax::visit;
use syntax::visit::{Visitor,fn_kind};
use syntax::ast::{fn_decl,Block,NodeId};
B
Brian Anderson 已提交
34

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

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;

N
Niko Matsakis 已提交
52
pub struct LoanDataFlowOperator;
53 54 55 56 57 58 59 60 61

/// XXX(pcwalton): Should just be #[deriving(Clone)], but that doesn't work
/// yet on unit structs.
impl Clone for LoanDataFlowOperator {
    fn clone(&self) -> LoanDataFlowOperator {
        LoanDataFlowOperator
    }
}

N
Niko Matsakis 已提交
62
pub type LoanDataFlow = DataFlowContext<LoanDataFlowOperator>;
B
Brian Anderson 已提交
63

64
impl Visitor<()> for BorrowckCtxt {
65
    fn visit_fn(&mut self, fk:&fn_kind, fd:&fn_decl,
66 67
                b:&Block, s:Span, n:NodeId, _:()) {
        borrowck_fn(self, fk, fd, b, s, n);
68 69 70
    }
}

71 72 73 74
pub fn check_crate(
    tcx: ty::ctxt,
    method_map: typeck::method_map,
    moves_map: moves::MovesMap,
N
Niko Matsakis 已提交
75
    moved_variables_set: moves::MovedVariablesSet,
76
    capture_map: moves::CaptureMap,
77
    crate: &ast::Crate) -> (root_map, write_guard_map)
78
{
79
    let mut bccx = BorrowckCtxt {
80 81 82
        tcx: tcx,
        method_map: method_map,
        moves_map: moves_map,
N
Niko Matsakis 已提交
83
        moved_variables_set: moved_variables_set,
84 85
        capture_map: capture_map,
        root_map: root_map(),
N
Niko Matsakis 已提交
86
        loan_map: @mut HashMap::new(),
87 88
        write_guard_map: @mut HashSet::new(),
        stmt_map: @mut HashSet::new(),
89 90 91 92 93 94 95 96
        stats: @mut BorrowStats {
            loaned_paths_same: 0,
            loaned_paths_imm: 0,
            stable_paths: 0,
            req_pure_paths: 0,
            guaranteed_paths: 0,
        }
    };
97
    let bccx = &mut bccx;
B
Brian Anderson 已提交
98

99
    visit::walk_crate(bccx, crate, ());
B
Brian Anderson 已提交
100 101

    if tcx.sess.borrowck_stats() {
102
        io::println("--- borrowck stats ---");
103 104 105 106 107 108 109 110 111 112
        printfln!("paths requiring guarantees: %u",
                  bccx.stats.guaranteed_paths);
        printfln!("paths requiring loans     : %s",
                  make_stat(bccx, bccx.stats.loaned_paths_same));
        printfln!("paths requiring imm loans : %s",
                  make_stat(bccx, bccx.stats.loaned_paths_imm));
        printfln!("stable paths              : %s",
                  make_stat(bccx, bccx.stats.stable_paths));
        printfln!("paths requiring purity    : %s",
                  make_stat(bccx, bccx.stats.req_pure_paths));
B
Brian Anderson 已提交
113 114
    }

N
Niko Matsakis 已提交
115
    return (bccx.root_map, bccx.write_guard_map);
B
Brian Anderson 已提交
116

117
    fn make_stat(bccx: &mut BorrowckCtxt, stat: uint) -> ~str {
B
Brian Anderson 已提交
118
        let stat_f = stat as float;
119
        let total = bccx.stats.guaranteed_paths as float;
B
Brian Anderson 已提交
120 121 122 123
        fmt!("%u (%.0f%%)", stat  , stat_f * 100f / total)
    }
}

124
fn borrowck_fn(this: &mut BorrowckCtxt,
125
               fk: &visit::fn_kind,
N
Niko Matsakis 已提交
126
               decl: &ast::fn_decl,
127
               body: &ast::Block,
128
               sp: Span,
129
               id: ast::NodeId) {
N
Niko Matsakis 已提交
130
    match fk {
131 132
        &visit::fk_anon(*) |
        &visit::fk_fn_block(*) => {
N
Niko Matsakis 已提交
133 134 135
            // Closures are checked as part of their containing fn item.
        }

136 137
        &visit::fk_item_fn(*) |
        &visit::fk_method(*) => {
N
Niko Matsakis 已提交
138 139 140
            debug!("borrowck_fn(id=%?)", id);

            // Check the body of fn items.
141
            let (id_range, all_loans, move_data) =
142
                gather_loans::gather_loans(this, decl, body);
143
            let mut loan_dfcx =
144 145
                DataFlowContext::new(this.tcx,
                                     this.method_map,
N
Niko Matsakis 已提交
146 147 148
                                     LoanDataFlowOperator,
                                     id_range,
                                     all_loans.len());
D
Daniel Micay 已提交
149
            for (loan_idx, loan) in all_loans.iter().enumerate() {
150 151
                loan_dfcx.add_gen(loan.gen_scope, loan_idx);
                loan_dfcx.add_kill(loan.kill_scope, loan_idx);
N
Niko Matsakis 已提交
152
            }
153 154 155 156 157 158 159 160 161 162
            loan_dfcx.propagate(body);

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

            check_loans::check_loans(this, &loan_dfcx, flowed_moves,
                                     *all_loans, body);
N
Niko Matsakis 已提交
163 164 165
        }
    }

166
    visit::walk_fn(this, fk, decl, body, sp, id, ());
N
Niko Matsakis 已提交
167 168
}

B
Brian Anderson 已提交
169 170 171
// ----------------------------------------------------------------------
// Type definitions

172 173 174 175
pub struct BorrowckCtxt {
    tcx: ty::ctxt,
    method_map: typeck::method_map,
    moves_map: moves::MovesMap,
N
Niko Matsakis 已提交
176
    moved_variables_set: moves::MovedVariablesSet,
177 178
    capture_map: moves::CaptureMap,
    root_map: root_map,
N
Niko Matsakis 已提交
179
    loan_map: LoanMap,
180 181 182 183
    write_guard_map: write_guard_map,
    stmt_map: stmt_set,

    // Statistics:
184 185 186 187 188 189 190 191 192
    stats: @mut BorrowStats
}

pub struct BorrowStats {
    loaned_paths_same: uint,
    loaned_paths_imm: uint,
    stable_paths: uint,
    req_pure_paths: uint,
    guaranteed_paths: uint
B
Brian Anderson 已提交
193 194
}

195
pub type LoanMap = @mut HashMap<ast::NodeId, @Loan>;
B
Brian Anderson 已提交
196

197 198 199 200 201 202 203 204 205 206 207 208 209
// The keys to the root map combine the `id` of the deref expression
// with the number of types that it is *autodereferenced*. So, for
// example, imagine I have a variable `x: @@@T` and an expression
// `(*x).f`.  This will have 3 derefs, one explicit and then two
// autoderefs. These are the relevant `root_map_key` values that could
// appear:
//
//    {id:*x, derefs:0} --> roots `x` (type: @@@T, due to explicit deref)
//    {id:*x, derefs:1} --> roots `*x` (type: @@T, due to autoderef #1)
//    {id:*x, derefs:2} --> roots `**x` (type: @T, due to autoderef #2)
//
// Note that there is no entry with derefs:3---the type of that expression
// is T, which is not a box.
210 211 212 213 214 215 216 217 218 219
//
// Note that implicit dereferences also occur with indexing of `@[]`,
// `@str`, etc.  The same rules apply. So, for example, given a
// variable `x` of type `@[@[...]]`, if I have an instance of the
// expression `x[0]` which is then auto-slice'd, there would be two
// potential entries in the root map, both with the id of the `x[0]`
// expression. The entry with `derefs==0` refers to the deref of `x`
// used as part of evaluating `x[0]`. The entry with `derefs==1`
// refers to the deref of the `x[0]` that occurs as part of the
// auto-slice.
N
Niko Matsakis 已提交
220
#[deriving(Eq, IterBytes)]
221
pub struct root_map_key {
222
    id: ast::NodeId,
223 224
    derefs: uint
}
B
Brian Anderson 已提交
225

226 227
// A set containing IDs of expressions of gc'd type that need to have a write
// guard.
228
pub type write_guard_map = @mut HashSet<root_map_key>;
229

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

232
#[deriving(Eq)]
N
Niko Matsakis 已提交
233 234 235
pub enum PartialTotal {
    Partial,   // Loan affects some portion
    Total      // Loan affects entire path
B
Brian Anderson 已提交
236 237
}

N
Niko Matsakis 已提交
238 239 240
///////////////////////////////////////////////////////////////////////////
// Loans and loan paths

241 242 243 244 245 246 247 248
#[deriving(Clone, Eq)]
pub enum LoanMutability {
    ImmutableMutability,
    ConstMutability,
    MutableMutability,
}

impl LoanMutability {
249
    pub fn from_ast_mutability(ast_mutability: ast::Mutability)
250 251
                               -> LoanMutability {
        match ast_mutability {
252 253
            ast::MutImmutable => ImmutableMutability,
            ast::MutMutable => MutableMutability,
254 255 256 257 258 259 260 261 262 263 264 265 266 267
        }
    }
}

impl ToStr for LoanMutability {
    fn to_str(&self) -> ~str {
        match *self {
            ImmutableMutability => ~"immutable",
            ConstMutability => ~"read-only",
            MutableMutability => ~"mutable",
        }
    }
}

N
Niko Matsakis 已提交
268 269 270 271 272
/// Record of a loan that was issued.
pub struct Loan {
    index: uint,
    loan_path: @LoanPath,
    cmt: mc::cmt,
273
    mutbl: LoanMutability,
N
Niko Matsakis 已提交
274
    restrictions: ~[Restriction],
275 276
    gen_scope: ast::NodeId,
    kill_scope: ast::NodeId,
277
    span: Span,
278 279
}

280
#[deriving(Eq, IterBytes)]
N
Niko Matsakis 已提交
281
pub enum LoanPath {
282
    LpVar(ast::NodeId),               // `x` in doc.rs
N
Niko Matsakis 已提交
283 284
    LpExtend(@LoanPath, mc::MutabilityCategory, LoanPathElem)
}
B
Brian Anderson 已提交
285

286
#[deriving(Eq, IterBytes)]
N
Niko Matsakis 已提交
287
pub enum LoanPathElem {
N
Niko Matsakis 已提交
288
    LpDeref(mc::PointerKind),    // `*LV` in doc.rs
289
    LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
290 291
}

292
impl LoanPath {
293
    pub fn node_id(&self) -> ast::NodeId {
N
Niko Matsakis 已提交
294 295 296 297 298
        match *self {
            LpVar(local_id) => local_id,
            LpExtend(base, _, _) => base.node_id()
        }
    }
299
}
B
Brian Anderson 已提交
300

N
Niko Matsakis 已提交
301 302 303 304 305 306 307 308
pub fn opt_loan_path(cmt: mc::cmt) -> Option<@LoanPath> {
    //! 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 {
309
        mc::cat_rvalue(*) |
N
Niko Matsakis 已提交
310
        mc::cat_static_item |
N
Niko Matsakis 已提交
311
        mc::cat_copied_upvar(_) => {
N
Niko Matsakis 已提交
312 313 314 315
            None
        }

        mc::cat_local(id) |
316
        mc::cat_arg(id) |
N
Niko Matsakis 已提交
317 318 319 320
        mc::cat_self(id) => {
            Some(@LpVar(id))
        }

N
Niko Matsakis 已提交
321
        mc::cat_deref(cmt_base, _, pk) => {
322
            do opt_loan_path(cmt_base).map_move |lp| {
N
Niko Matsakis 已提交
323
                @LpExtend(lp, cmt.mutbl, LpDeref(pk))
324
            }
N
Niko Matsakis 已提交
325 326 327
        }

        mc::cat_interior(cmt_base, ik) => {
328 329 330
            do opt_loan_path(cmt_base).map_move |lp| {
                @LpExtend(lp, cmt.mutbl, LpInterior(ik))
            }
N
Niko Matsakis 已提交
331 332
        }

333
        mc::cat_downcast(cmt_base) |
N
Niko Matsakis 已提交
334 335 336 337 338
        mc::cat_stack_upvar(cmt_base) |
        mc::cat_discr(cmt_base, _) => {
            opt_loan_path(cmt_base)
        }
    }
339
}
B
Brian Anderson 已提交
340

N
Niko Matsakis 已提交
341 342 343 344 345 346
///////////////////////////////////////////////////////////////////////////
// Restrictions
//
// Borrowing an lvalue often results in *restrictions* that limit what
// can be done with this lvalue during the scope of the loan:
//
347 348 349 350
// - `RESTR_MUTATE`: The lvalue may not be modified.
// - `RESTR_CLAIM`: `&mut` borrows of the lvalue are forbidden.
// - `RESTR_FREEZE`: `&` borrows of the lvalue are forbidden.
// - `RESTR_ALIAS`: All borrows of the lvalue are forbidden.
N
Niko Matsakis 已提交
351 352 353 354 355 356 357 358
//
// 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 {
    loan_path: @LoanPath,
    set: RestrictionSet
359 360
}

361
#[deriving(Eq)]
N
Niko Matsakis 已提交
362 363
pub struct RestrictionSet {
    bits: u32
B
Brian Anderson 已提交
364 365
}

366 367 368 369 370
pub static RESTR_EMPTY: RestrictionSet  = RestrictionSet {bits: 0b0000};
pub static RESTR_MUTATE: RestrictionSet = RestrictionSet {bits: 0b0001};
pub static RESTR_CLAIM: RestrictionSet  = RestrictionSet {bits: 0b0010};
pub static RESTR_FREEZE: RestrictionSet = RestrictionSet {bits: 0b0100};
pub static RESTR_ALIAS: RestrictionSet  = RestrictionSet {bits: 0b1000};
N
Niko Matsakis 已提交
371

372 373
impl RestrictionSet {
    pub fn intersects(&self, restr: RestrictionSet) -> bool {
N
Niko Matsakis 已提交
374
        (self.bits & restr.bits) != 0
375 376
    }

377
    pub fn contains_all(&self, restr: RestrictionSet) -> bool {
N
Niko Matsakis 已提交
378
        (self.bits & restr.bits) == restr.bits
379 380 381
    }
}

N
Niko Matsakis 已提交
382 383 384 385 386
impl BitOr<RestrictionSet,RestrictionSet> for RestrictionSet {
    fn bitor(&self, rhs: &RestrictionSet) -> RestrictionSet {
        RestrictionSet {bits: self.bits | rhs.bits}
    }
}
B
Brian Anderson 已提交
387

N
Niko Matsakis 已提交
388 389 390
impl BitAnd<RestrictionSet,RestrictionSet> for RestrictionSet {
    fn bitand(&self, rhs: &RestrictionSet) -> RestrictionSet {
        RestrictionSet {bits: self.bits & rhs.bits}
B
Brian Anderson 已提交
391 392 393
    }
}

N
Niko Matsakis 已提交
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413
///////////////////////////////////////////////////////////////////////////
// Rooting of managed boxes
//
// When we borrow the interior of a managed box, it is sometimes
// necessary to *root* the box, meaning to stash a copy of the box
// somewhere that the garbage collector will find it. This ensures
// that the box is not collected for the lifetime of the borrow.
//
// As part of this rooting, we sometimes also freeze the box at
// runtime, meaning that we dynamically detect when the box is
// borrowed in incompatible ways.
//
// Both of these actions are driven through the `root_map`, which maps
// from a node to the dynamic rooting action that should be taken when
// that node executes. The node is identified through a
// `root_map_key`, which pairs a node-id and a deref count---the
// problem is that sometimes the box that needs to be rooted is only
// uncovered after a certain number of auto-derefs.

pub struct RootInfo {
414
    scope: ast::NodeId,
N
Niko Matsakis 已提交
415 416 417 418 419
    freeze: Option<DynaFreezeKind> // Some() if we should freeze box at runtime
}

pub type root_map = @mut HashMap<root_map_key, RootInfo>;

420
pub fn root_map() -> root_map {
421
    return @mut HashMap::new();
B
Brian Anderson 已提交
422 423
}

N
Niko Matsakis 已提交
424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443
pub enum DynaFreezeKind {
    DynaImm,
    DynaMut
}

impl ToStr for DynaFreezeKind {
    fn to_str(&self) -> ~str {
        match *self {
            DynaMut => ~"mutable",
            DynaImm => ~"immutable"
        }
    }
}

///////////////////////////////////////////////////////////////////////////
// Errors

// Errors that can occur
#[deriving(Eq)]
pub enum bckerr_code {
444
    err_mutbl(LoanMutability),
N
Niko Matsakis 已提交
445 446 447 448 449 450 451 452 453
    err_out_of_root_scope(ty::Region, ty::Region), // superscope, subscope
    err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
    err_freeze_aliasable_const
}

// Combination of an error code and the categorization of the expression
// that caused it
#[deriving(Eq)]
pub struct BckError {
454
    span: Span,
N
Niko Matsakis 已提交
455 456 457 458 459 460 461 462 463
    cmt: mc::cmt,
    code: bckerr_code
}

pub enum AliasableViolationKind {
    MutabilityViolation,
    BorrowViolation
}

464 465 466 467 468
pub enum MovedValueUseKind {
    MovedInUse,
    MovedInCapture,
}

N
Niko Matsakis 已提交
469
///////////////////////////////////////////////////////////////////////////
B
Brian Anderson 已提交
470 471
// Misc

472 473 474
impl BorrowckCtxt {
    pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
                           -> bool {
475
        self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
B
Brian Anderson 已提交
476 477
    }

478
    pub fn is_subscope_of(&self, r_sub: ast::NodeId, r_sup: ast::NodeId)
479
                          -> bool {
N
Niko Matsakis 已提交
480 481 482
        self.tcx.region_maps.is_subscope_of(r_sub, r_sup)
    }

483
    pub fn is_move(&self, id: ast::NodeId) -> bool {
484 485 486
        self.moves_map.contains(&id)
    }

487
    pub fn cat_expr(&self, expr: @ast::Expr) -> mc::cmt {
N
Niko Matsakis 已提交
488
        mc::cat_expr(self.tcx, self.method_map, expr)
B
Brian Anderson 已提交
489 490
    }

491
    pub fn cat_expr_unadjusted(&self, expr: @ast::Expr) -> mc::cmt {
N
Niko Matsakis 已提交
492
        mc::cat_expr_unadjusted(self.tcx, self.method_map, expr)
B
Brian Anderson 已提交
493 494
    }

495
    pub fn cat_expr_autoderefd(&self,
496
                               expr: @ast::Expr,
497 498
                               adj: @ty::AutoAdjustment)
                               -> mc::cmt {
499 500 501
        match *adj {
            ty::AutoAddEnv(*) => {
                // no autoderefs
N
Niko Matsakis 已提交
502
                mc::cat_expr_unadjusted(self.tcx, self.method_map, expr)
503 504 505 506 507
            }

            ty::AutoDerefRef(
                ty::AutoDerefRef {
                    autoderefs: autoderefs, _}) => {
N
Niko Matsakis 已提交
508 509
                mc::cat_expr_autoderefd(self.tcx, self.method_map, expr,
                                        autoderefs)
510 511
            }
        }
B
Brian Anderson 已提交
512 513
    }

514
    pub fn cat_def(&self,
515
                   id: ast::NodeId,
516
                   span: Span,
517
                   ty: ty::t,
518
                   def: ast::Def)
519
                   -> mc::cmt {
N
Niko Matsakis 已提交
520
        mc::cat_def(self.tcx, self.method_map, id, span, ty, def)
B
Brian Anderson 已提交
521 522
    }

523
    pub fn cat_discr(&self, cmt: mc::cmt, match_id: ast::NodeId) -> mc::cmt {
N
Niko Matsakis 已提交
524 525 526
        @mc::cmt_ {cat:mc::cat_discr(cmt, match_id),
                   mutbl:cmt.mutbl.inherit(),
                   ..*cmt}
B
Brian Anderson 已提交
527 528
    }

529
    pub fn mc_ctxt(&self) -> mc::mem_categorization_ctxt {
N
Niko Matsakis 已提交
530
        mc::mem_categorization_ctxt {tcx: self.tcx,
531
                                     method_map: self.method_map}
532 533
    }

534 535
    pub fn cat_pattern(&self,
                       cmt: mc::cmt,
536 537
                       pat: @ast::Pat,
                       op: &fn(mc::cmt, @ast::Pat)) {
538
        let mc = self.mc_ctxt();
B
Brian Anderson 已提交
539 540 541
        mc.cat_pattern(cmt, pat, op);
    }

542
    pub fn report(&self, err: BckError) {
B
Brian Anderson 已提交
543
        self.span_err(
N
Niko Matsakis 已提交
544 545
            err.span,
            self.bckerr_to_str(err));
B
Brian Anderson 已提交
546 547 548
        self.note_and_explain_bckerr(err);
    }

549
    pub fn report_use_of_moved_value(&self,
550
                                     use_span: Span,
551
                                     use_kind: MovedValueUseKind,
552
                                     lp: &LoanPath,
553 554
                                     move: &move_data::Move,
                                     moved_lp: @LoanPath) {
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
        let verb = match use_kind {
            MovedInUse => "use",
            MovedInCapture => "capture",
        };

        match move.kind {
            move_data::Declared => {
                self.tcx.sess.span_err(
                    use_span,
                    fmt!("%s of possibly uninitialized value: `%s`",
                         verb,
                         self.loan_path_to_str(lp)));
            }
            _ => {
                let partially = if lp == moved_lp {""} else {"partially "};
                self.tcx.sess.span_err(
                    use_span,
                    fmt!("%s of %smoved value: `%s`",
                         verb,
                         partially,
                         self.loan_path_to_str(lp)));
            }
        }

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

            move_data::MoveExpr(expr) => {
                let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
584 585
                let suggestion = move_suggestion(self.tcx, expr_ty,
                        "moved by default (use `copy` to override)");
586 587
                self.tcx.sess.span_note(
                    expr.span,
588
                    fmt!("`%s` moved here because it has type `%s`, which is %s",
589
                         self.loan_path_to_str(moved_lp),
590
                         expr_ty.user_string(self.tcx), suggestion));
591 592 593 594 595 596 597 598 599 600 601 602 603
            }

            move_data::MovePat(pat) => {
                let pat_ty = ty::node_id_to_type(self.tcx, pat.id);
                self.tcx.sess.span_note(
                    pat.span,
                    fmt!("`%s` moved here because it has type `%s`, \
                          which is moved by default (use `ref` to override)",
                         self.loan_path_to_str(moved_lp),
                         pat_ty.user_string(self.tcx)));
            }

            move_data::Captured(expr) => {
604 605 606 607
                let expr_ty = ty::expr_ty_adjusted(self.tcx, expr);
                let suggestion = move_suggestion(self.tcx, expr_ty,
                        "moved by default (make a copy and \
                         capture that instead to override)");
608 609
                self.tcx.sess.span_note(
                    expr.span,
610 611 612 613 614 615 616 617 618 619 620 621 622
                    fmt!("`%s` moved into closure environment here because it \
                          has type `%s`, which is %s",
                         self.loan_path_to_str(moved_lp),
                         expr_ty.user_string(self.tcx), suggestion));
            }
        }

        fn move_suggestion(tcx: ty::ctxt, ty: ty::t, default_msg: &'static str)
                          -> &'static str {
            match ty::get(ty).sty {
                ty::ty_closure(ref cty) if cty.sigil == ast::BorrowedSigil =>
                    "a non-copyable stack closure (capture it in a new closure, \
                     e.g. `|x| f(x)`, to override)",
623
                _ if ty::type_moves_by_default(tcx, ty) =>
624 625
                    "non-copyable (perhaps you meant to use clone()?)",
                _ => default_msg,
626 627 628 629
            }
        }
    }

630
    pub fn report_reassigned_immutable_variable(&self,
631
                                                span: Span,
632
                                                lp: &LoanPath,
633 634
                                                assign:
                                                &move_data::Assignment) {
635 636 637 638 639 640 641 642 643
        self.tcx.sess.span_err(
            span,
            fmt!("re-assignment of immutable variable `%s`",
                 self.loan_path_to_str(lp)));
        self.tcx.sess.span_note(
            assign.span,
            fmt!("prior assignment occurs here"));
    }

644
    pub fn span_err(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
645 646 647
        self.tcx.sess.span_err(s, m);
    }

648
    pub fn span_note(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
649 650 651
        self.tcx.sess.span_note(s, m);
    }

652
    pub fn bckerr_to_str(&self, err: BckError) -> ~str {
B
Brian Anderson 已提交
653
        match err.code {
654
            err_mutbl(lk) => {
N
Niko Matsakis 已提交
655 656 657 658
                fmt!("cannot borrow %s %s as %s",
                     err.cmt.mutbl.to_user_str(),
                     self.cmt_to_str(err.cmt),
                     self.mut_to_str(lk))
B
Brian Anderson 已提交
659
            }
N
Niko Matsakis 已提交
660 661
            err_out_of_root_scope(*) => {
                fmt!("cannot root managed value long enough")
B
Brian Anderson 已提交
662
            }
N
Niko Matsakis 已提交
663 664
            err_out_of_scope(*) => {
                fmt!("borrowed value does not live long enough")
B
Brian Anderson 已提交
665
            }
N
Niko Matsakis 已提交
666 667 668 669 670 671
            err_freeze_aliasable_const => {
                // Means that the user borrowed a ~T or enum value
                // residing in &const or @const pointer.  Terrible
                // error message, but then &const and @const are
                // supposed to be going away.
                fmt!("unsafe borrow of aliasable, const value")
B
Brian Anderson 已提交
672
            }
N
Niko Matsakis 已提交
673 674 675
        }
    }

676
    pub fn report_aliasability_violation(&self,
677
                                         span: Span,
678 679
                                         kind: AliasableViolationKind,
                                         cause: mc::AliasableReason) {
N
Niko Matsakis 已提交
680 681 682 683 684 685 686 687 688 689
        let prefix = match kind {
            MutabilityViolation => "cannot assign to an `&mut`",
            BorrowViolation => "cannot borrow an `&mut`"
        };

        match cause {
            mc::AliasableOther => {
                self.tcx.sess.span_err(
                    span,
                    fmt!("%s in an aliasable location", prefix));
B
Brian Anderson 已提交
690
            }
691
            mc::AliasableManaged(ast::MutMutable) => {
N
Niko Matsakis 已提交
692
                // FIXME(#6269) reborrow @mut to &mut
N
Niko Matsakis 已提交
693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712
                self.tcx.sess.span_err(
                    span,
                    fmt!("%s in a `@mut` pointer; \
                          try borrowing as `&mut` first", prefix));
            }
            mc::AliasableManaged(m) => {
                self.tcx.sess.span_err(
                    span,
                    fmt!("%s in a `@%s` pointer; \
                          try an `@mut` instead",
                         prefix,
                         self.mut_to_keyword(m)));
            }
            mc::AliasableBorrowed(m) => {
                self.tcx.sess.span_err(
                    span,
                    fmt!("%s in a `&%s` pointer; \
                          try an `&mut` instead",
                         prefix,
                         self.mut_to_keyword(m)));
B
Brian Anderson 已提交
713 714 715 716
            }
        }
    }

717
    pub fn note_and_explain_bckerr(&self, err: BckError) {
B
Brian Anderson 已提交
718 719
        let code = err.code;
        match code {
N
Niko Matsakis 已提交
720
            err_mutbl(*) | err_freeze_aliasable_const(*) => {}
B
Brian Anderson 已提交
721 722 723 724

            err_out_of_root_scope(super_scope, sub_scope) => {
                note_and_explain_region(
                    self.tcx,
725
                    "managed value would have to be rooted for ",
B
Brian Anderson 已提交
726
                    sub_scope,
727
                    "...");
B
Brian Anderson 已提交
728 729
                note_and_explain_region(
                    self.tcx,
730
                    "...but can only be rooted for ",
B
Brian Anderson 已提交
731
                    super_scope,
732
                    "");
B
Brian Anderson 已提交
733 734 735 736 737
            }

            err_out_of_scope(super_scope, sub_scope) => {
                note_and_explain_region(
                    self.tcx,
738
                    "borrowed pointer must be valid for ",
B
Brian Anderson 已提交
739
                    sub_scope,
740
                    "...");
B
Brian Anderson 已提交
741 742
                note_and_explain_region(
                    self.tcx,
743
                    "...but borrowed value is only valid for ",
B
Brian Anderson 已提交
744
                    super_scope,
745
                    "");
B
Brian Anderson 已提交
746 747 748 749
          }
        }
    }

750 751 752
    pub fn append_loan_path_to_str_from_interior(&self,
                                                 loan_path: &LoanPath,
                                                 out: &mut ~str) {
N
Niko Matsakis 已提交
753
        match *loan_path {
N
Niko Matsakis 已提交
754
            LpExtend(_, _, LpDeref(_)) => {
755
                out.push_char('(');
N
Niko Matsakis 已提交
756
                self.append_loan_path_to_str(loan_path, out);
757
                out.push_char(')');
N
Niko Matsakis 已提交
758 759 760 761 762 763 764 765
            }
            LpExtend(_, _, LpInterior(_)) |
            LpVar(_) => {
                self.append_loan_path_to_str(loan_path, out);
            }
        }
    }

766 767 768
    pub fn append_loan_path_to_str(&self,
                                   loan_path: &LoanPath,
                                   out: &mut ~str) {
N
Niko Matsakis 已提交
769 770 771
        match *loan_path {
            LpVar(id) => {
                match self.tcx.items.find(&id) {
772
                    Some(&ast_map::node_local(ref ident)) => {
773
                        out.push_str(token::ident_to_str(ident));
N
Niko Matsakis 已提交
774 775 776 777 778 779 780 781
                    }
                    r => {
                        self.tcx.sess.bug(
                            fmt!("Loan path LpVar(%?) maps to %?, not local",
                                 id, r));
                    }
                }
            }
B
Brian Anderson 已提交
782

783
            LpExtend(lp_base, _, LpInterior(mc::InteriorField(fname))) => {
N
Niko Matsakis 已提交
784
                self.append_loan_path_to_str_from_interior(lp_base, out);
785
                match fname {
786
                    mc::NamedField(ref fname) => {
787
                        out.push_char('.');
788
                        out.push_str(token::interner_get(*fname));
789 790
                    }
                    mc::PositionalField(idx) => {
791 792
                        out.push_char('#'); // invent a notation here
                        out.push_str(idx.to_str());
793 794
                    }
                }
N
Niko Matsakis 已提交
795 796
            }

797
            LpExtend(lp_base, _, LpInterior(mc::InteriorElement(_))) => {
N
Niko Matsakis 已提交
798
                self.append_loan_path_to_str_from_interior(lp_base, out);
799
                out.push_str("[]");
N
Niko Matsakis 已提交
800 801
            }

N
Niko Matsakis 已提交
802
            LpExtend(lp_base, _, LpDeref(_)) => {
803
                out.push_char('*');
N
Niko Matsakis 已提交
804 805 806 807 808
                self.append_loan_path_to_str(lp_base, out);
            }
        }
    }

809
    pub fn loan_path_to_str(&self, loan_path: &LoanPath) -> ~str {
N
Niko Matsakis 已提交
810 811 812
        let mut result = ~"";
        self.append_loan_path_to_str(loan_path, &mut result);
        result
B
Brian Anderson 已提交
813 814
    }

815
    pub fn cmt_to_str(&self, cmt: mc::cmt) -> ~str {
N
Niko Matsakis 已提交
816 817 818
        let mc = &mc::mem_categorization_ctxt {tcx: self.tcx,
                                               method_map: self.method_map};
        mc.cmt_to_str(cmt)
B
Brian Anderson 已提交
819 820
    }

821 822
    pub fn mut_to_str(&self, mutbl: LoanMutability) -> ~str {
        mutbl.to_str()
B
Brian Anderson 已提交
823 824
    }

825
    pub fn mut_to_keyword(&self, mutbl: ast::Mutability) -> &'static str {
N
Niko Matsakis 已提交
826
        match mutbl {
827 828
            ast::MutImmutable => "",
            ast::MutMutable => "mut",
829 830
        }
    }
N
Niko Matsakis 已提交
831 832 833
}

impl DataFlowOperator for LoanDataFlowOperator {
834
    #[inline]
N
Niko Matsakis 已提交
835 836 837 838
    fn initial_value(&self) -> bool {
        false // no loans in scope by default
    }

839
    #[inline]
N
Niko Matsakis 已提交
840 841 842 843
    fn join(&self, succ: uint, pred: uint) -> uint {
        succ | pred // loans from both preds are in scope
    }

844
    #[inline]
N
Niko Matsakis 已提交
845 846 847 848
    fn walk_closures(&self) -> bool {
        true
    }
}
849

N
Niko Matsakis 已提交
850 851 852 853 854 855 856 857 858
impl Repr for Loan {
    fn repr(&self, tcx: ty::ctxt) -> ~str {
        fmt!("Loan_%?(%s, %?, %?-%?, %s)",
             self.index,
             self.loan_path.repr(tcx),
             self.mutbl,
             self.gen_scope,
             self.kill_scope,
             self.restrictions.repr(tcx))
B
Brian Anderson 已提交
859 860 861
    }
}

N
Niko Matsakis 已提交
862 863 864 865 866 867 868 869 870 871 872 873 874 875 876
impl Repr for Restriction {
    fn repr(&self, tcx: ty::ctxt) -> ~str {
        fmt!("Restriction(%s, %x)",
             self.loan_path.repr(tcx),
             self.set.bits as uint)
    }
}

impl Repr for LoanPath {
    fn repr(&self, tcx: ty::ctxt) -> ~str {
        match self {
            &LpVar(id) => {
                fmt!("$(%?)", id)
            }

N
Niko Matsakis 已提交
877
            &LpExtend(lp, _, LpDeref(_)) => {
N
Niko Matsakis 已提交
878 879 880 881 882 883 884
                fmt!("%s.*", lp.repr(tcx))
            }

            &LpExtend(lp, _, LpInterior(ref interior)) => {
                fmt!("%s.%s", lp.repr(tcx), interior.repr(tcx))
            }
        }
B
Brian Anderson 已提交
885 886
    }
}