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

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

E
Eduard Burtescu 已提交
24
use std::rc::Rc;
25
use std::string::String;
26
use syntax::ast;
N
Niko Matsakis 已提交
27
use syntax::ast_map;
28
use syntax::ast_map::blocks::{FnLikeNode, FnParts};
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 graphviz;

53 54
pub mod move_data;

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

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

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

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

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

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

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

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

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

119
/// Collection of conclusions determined via borrow checker analyses.
120
pub struct AnalysisData<'a, 'tcx: 'a> {
121
    pub all_loans: Vec<Loan>,
122 123
    pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
    pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
124 125
}

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

    check_loans::check_loans(this, &loan_dfcx, flowed_moves,
                             all_loans.as_slice(), decl, body);

142
    visit::walk_fn(this, fk, decl, body, sp);
143
}
144

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

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

    let flowed_moves = move_data::FlowedMoveData::new(move_data,
                                                      this.tcx,
174
                                                      cfg,
175
                                                      id_range,
176
                                                      decl,
177 178
                                                      body);

179 180 181 182
    AnalysisData { all_loans: all_loans,
                   loans: loan_dfcx,
                   move_data:flowed_moves }
}
N
Niko Matsakis 已提交
183

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

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

    let p = input.fn_parts;

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

    (bccx, dataflow_data)
N
Niko Matsakis 已提交
225 226
}

B
Brian Anderson 已提交
227 228 229
// ----------------------------------------------------------------------
// Type definitions

230 231
pub struct BorrowckCtxt<'a, 'tcx: 'a> {
    tcx: &'a ty::ctxt<'tcx>,
F
Flavio Percoco 已提交
232 233

    // Statistics:
234
    stats: BorrowStats
B
Brian Anderson 已提交
235 236
}

237 238 239 240 241
struct BorrowStats {
    loaned_paths_same: uint,
    loaned_paths_imm: uint,
    stable_paths: uint,
    guaranteed_paths: uint
242
}
B
Brian Anderson 已提交
243

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

N
Niko Matsakis 已提交
246 247 248 249 250 251
///////////////////////////////////////////////////////////////////////////
// Loans and loan paths

/// Record of a loan that was issued.
pub struct Loan {
    index: uint,
E
Eduard Burtescu 已提交
252
    loan_path: Rc<LoanPath>,
253
    kind: ty::BorrowKind,
C
Cameron Zwarich 已提交
254
    restricted_paths: Vec<Rc<LoanPath>>,
255 256
    gen_scope: ast::NodeId,
    kill_scope: ast::NodeId,
257
    span: Span,
258
    cause: euv::LoanCause,
259 260
}

261 262 263 264 265 266
impl Loan {
    pub fn loan_path(&self) -> Rc<LoanPath> {
        self.loan_path.clone()
    }
}

L
Luqman Aden 已提交
267
#[deriving(PartialEq, Eq, Hash, Show)]
N
Niko Matsakis 已提交
268
pub enum LoanPath {
269
    LpVar(ast::NodeId),               // `x` in doc.rs
270
    LpUpvar(ty::UpvarId),             // `x` captured by-value into closure
E
Eduard Burtescu 已提交
271
    LpExtend(Rc<LoanPath>, mc::MutabilityCategory, LoanPathElem)
N
Niko Matsakis 已提交
272
}
B
Brian Anderson 已提交
273

L
Luqman Aden 已提交
274
#[deriving(PartialEq, Eq, Hash, Show)]
N
Niko Matsakis 已提交
275
pub enum LoanPathElem {
N
Niko Matsakis 已提交
276
    LpDeref(mc::PointerKind),    // `*LV` in doc.rs
277
    LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
278 279
}

280 281 282 283
pub fn closure_to_block(closure_id: ast::NodeId,
                    tcx: &ty::ctxt) -> ast::NodeId {
    match tcx.map.get(closure_id) {
        ast_map::NodeExpr(expr) => match expr.node {
284 285 286
            ast::ExprProc(_, ref block) |
            ast::ExprFnBlock(_, _, ref block) |
            ast::ExprUnboxedFn(_, _, _, ref block) => { block.id }
S
Steve Klabnik 已提交
287
            _ => panic!("encountered non-closure id: {}", closure_id)
288
        },
S
Steve Klabnik 已提交
289
        _ => panic!("encountered non-expr id: {}", closure_id)
290 291 292
    }
}

293
impl LoanPath {
294
    pub fn kill_scope(&self, tcx: &ty::ctxt) -> ast::NodeId {
N
Niko Matsakis 已提交
295
        match *self {
296 297 298 299
            LpVar(local_id) => tcx.region_maps.var_scope(local_id),
            LpUpvar(upvar_id) =>
                closure_to_block(upvar_id.closure_expr_id, tcx),
            LpExtend(ref base, _, _) => base.kill_scope(tcx),
N
Niko Matsakis 已提交
300 301
        }
    }
P
P1start 已提交
302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346

    fn has_fork(&self, other: &LoanPath) -> bool {
        match (self, other) {
            (&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 {
        match *self {
            LpExtend(ref base, _, LpDeref(_)) => base.depth(),
            LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
            _ => 0,
        }
    }

    fn common(&self, other: &LoanPath) -> Option<LoanPath> {
        match (self, other) {
            (&LpExtend(ref base, a, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
                if id == id2 {
                    base.common(&**base2).map(|x| {
                        let xd = x.depth();
                        if base.depth() == xd && base2.depth() == xd {
                            LpExtend(Rc::new(x), a, LpInterior(id))
                        } else {
                            x
                        }
                    })
                } else {
                    base.common(&**base2)
                },
            (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
            (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
            (&LpVar(id), &LpVar(id2)) => if id == id2 { Some(LpVar(id)) } else { None },
            (&LpUpvar(id), &LpUpvar(id2)) => if id == id2 { Some(LpUpvar(id)) } else { None },
            _ => None,
        }
    }
347
}
B
Brian Anderson 已提交
348

349
pub fn opt_loan_path(cmt: &mc::cmt) -> Option<Rc<LoanPath>> {
N
Niko Matsakis 已提交
350 351 352 353 354 355 356
    //! 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 已提交
357
        mc::cat_rvalue(..) |
358 359 360 361
        mc::cat_static_item => {
            None
        }

362
        mc::cat_local(id) => {
E
Eduard Burtescu 已提交
363
            Some(Rc::new(LpVar(id)))
N
Niko Matsakis 已提交
364 365
        }

366 367
        mc::cat_upvar(mc::Upvar { id, .. }) => {
            Some(Rc::new(LpUpvar(id)))
368 369
        }

370
        mc::cat_deref(ref cmt_base, _, pk) => {
371
            opt_loan_path(cmt_base).map(|lp| {
E
Eduard Burtescu 已提交
372
                Rc::new(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
373
            })
N
Niko Matsakis 已提交
374 375
        }

376
        mc::cat_interior(ref cmt_base, ik) => {
377
            opt_loan_path(cmt_base).map(|lp| {
E
Eduard Burtescu 已提交
378
                Rc::new(LpExtend(lp, cmt.mutbl, LpInterior(ik)))
379
            })
N
Niko Matsakis 已提交
380 381
        }

A
Ariel Ben-Yehuda 已提交
382
        mc::cat_downcast(ref cmt_base) => {
N
Niko Matsakis 已提交
383 384 385
            opt_loan_path(cmt_base)
        }
    }
386
}
B
Brian Anderson 已提交
387

N
Niko Matsakis 已提交
388 389 390 391
///////////////////////////////////////////////////////////////////////////
// Errors

// Errors that can occur
392
#[deriving(PartialEq)]
N
Niko Matsakis 已提交
393
pub enum bckerr_code {
394
    err_mutbl,
N
Niko Matsakis 已提交
395
    err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
396
    err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
N
Niko Matsakis 已提交
397 398 399 400
}

// Combination of an error code and the categorization of the expression
// that caused it
401
#[deriving(PartialEq)]
N
Niko Matsakis 已提交
402
pub struct BckError {
403
    span: Span,
404
    cause: euv::LoanCause,
N
Niko Matsakis 已提交
405 406 407 408 409 410
    cmt: mc::cmt,
    code: bckerr_code
}

pub enum AliasableViolationKind {
    MutabilityViolation,
411
    BorrowViolation(euv::LoanCause)
N
Niko Matsakis 已提交
412 413
}

L
Luqman Aden 已提交
414
#[deriving(Show)]
415 416 417 418 419
pub enum MovedValueUseKind {
    MovedInUse,
    MovedInCapture,
}

N
Niko Matsakis 已提交
420
///////////////////////////////////////////////////////////////////////////
B
Brian Anderson 已提交
421 422
// Misc

423
impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
424 425
    pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
                           -> bool {
426
        self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
B
Brian Anderson 已提交
427 428
    }

429
    pub fn mc(&self) -> mc::MemCategorizationContext<'a, ty::ctxt<'tcx>> {
430
        mc::MemCategorizationContext::new(self.tcx)
431 432
    }

E
Eduard Burtescu 已提交
433
    pub fn cat_expr(&self, expr: &ast::Expr) -> mc::cmt {
434 435 436 437 438 439
        match self.mc().cat_expr(expr) {
            Ok(c) => c,
            Err(()) => {
                self.tcx.sess.span_bug(expr.span, "error in mem categorization");
            }
        }
B
Brian Anderson 已提交
440 441
    }

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

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

460
        let (ol, moved_lp_msg) = match the_move.kind {
461 462 463
            move_data::Declared => {
                self.tcx.sess.span_err(
                    use_span,
464
                    format!("{} of possibly uninitialized variable: `{}`",
465
                            verb,
466
                            self.loan_path_to_string(lp)).as_slice());
467 468
                (self.loan_path_to_string(moved_lp),
                 String::new())
469 470
            }
            _ => {
471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502
                // 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 { "" };
503 504
                self.tcx.sess.span_err(
                    use_span,
A
Alex Crichton 已提交
505
                    format!("{} of {}moved value: `{}`",
506
                            verb,
507 508 509
                            msg,
                            nl).as_slice());
                (ol, moved_lp_msg)
510
            }
511
        };
512

513
        match the_move.kind {
514 515
            move_data::Declared => {}

E
Eduard Burtescu 已提交
516
            move_data::MoveExpr => {
517 518 519
                let (expr_ty, expr_span) = match self.tcx
                                                     .map
                                                     .find(the_move.id) {
520
                    Some(ast_map::NodeExpr(expr)) => {
521
                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
E
Eduard Burtescu 已提交
522
                    }
523
                    r => {
L
Luqman Aden 已提交
524 525
                        self.tcx.sess.bug(format!("MoveExpr({}) maps to \
                                                   {}, not Expr",
526
                                                  the_move.id,
527 528
                                                  r).as_slice())
                    }
E
Eduard Burtescu 已提交
529
                };
P
P1start 已提交
530 531
                let (suggestion, _) = move_suggestion(self.tcx, expr_ty,
                        ("moved by default", ""));
532
                self.tcx.sess.span_note(
E
Eduard Burtescu 已提交
533
                    expr_span,
534 535 536
                    format!("`{}` moved here{} because it has type `{}`, which is {}",
                            ol,
                            moved_lp_msg,
537 538
                            expr_ty.user_string(self.tcx),
                            suggestion).as_slice());
539 540
            }

E
Eduard Burtescu 已提交
541
            move_data::MovePat => {
542
                let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
P
P1start 已提交
543 544
                let span = self.tcx.map.span(the_move.id);
                self.tcx.sess.span_note(span,
545
                    format!("`{}` moved here{} because it has type `{}`, \
P
P1start 已提交
546
                             which is moved by default",
547 548
                            ol,
                            moved_lp_msg,
549
                            pat_ty.user_string(self.tcx)).as_slice());
P
P1start 已提交
550 551
                self.tcx.sess.span_help(span,
                    "use `ref` to override");
552 553
            }

E
Eduard Burtescu 已提交
554
            move_data::Captured => {
555 556 557
                let (expr_ty, expr_span) = match self.tcx
                                                     .map
                                                     .find(the_move.id) {
558
                    Some(ast_map::NodeExpr(expr)) => {
559
                        (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
E
Eduard Burtescu 已提交
560
                    }
561
                    r => {
L
Luqman Aden 已提交
562 563
                        self.tcx.sess.bug(format!("Captured({}) maps to \
                                                   {}, not Expr",
564
                                                  the_move.id,
565 566
                                                  r).as_slice())
                    }
E
Eduard Burtescu 已提交
567
                };
P
P1start 已提交
568 569 570
                let (suggestion, help) = move_suggestion(self.tcx, expr_ty,
                        ("moved by default", "make a copy and \
                         capture that instead to override"));
571
                self.tcx.sess.span_note(
E
Eduard Burtescu 已提交
572
                    expr_span,
573
                    format!("`{}` moved into closure environment here{} because it \
574
                            has type `{}`, which is {}",
575 576
                            ol,
                            moved_lp_msg,
577 578
                            expr_ty.user_string(self.tcx),
                            suggestion).as_slice());
P
P1start 已提交
579
                self.tcx.sess.span_help(expr_span, help);
580 581 582
            }
        }

P
P1start 已提交
583 584
        fn move_suggestion(tcx: &ty::ctxt, ty: ty::t, default_msgs: (&'static str, &'static str))
                          -> (&'static str, &'static str) {
585
            match ty::get(ty).sty {
586 587 588 589
                ty::ty_closure(box ty::ClosureTy {
                        store: ty::RegionTraitStore(..),
                        ..
                    }) =>
P
P1start 已提交
590 591
                    ("a non-copyable stack closure",
                     "capture it in a new closure, e.g. `|x| f(x)`, to override"),
592
                _ if ty::type_moves_by_default(tcx, ty) =>
P
P1start 已提交
593 594 595
                    ("non-copyable",
                     "perhaps you meant to use `clone()`?"),
                _ => default_msgs,
596 597 598 599
            }
        }
    }

600
    pub fn report_reassigned_immutable_variable(&self,
601
                                                span: Span,
602
                                                lp: &LoanPath,
603 604
                                                assign:
                                                &move_data::Assignment) {
605 606
        self.tcx.sess.span_err(
            span,
A
Alex Crichton 已提交
607
            format!("re-assignment of immutable variable `{}`",
608
                    self.loan_path_to_string(lp)).as_slice());
609
        self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
610 611
    }

612
    pub fn span_err(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
613 614 615
        self.tcx.sess.span_err(s, m);
    }

616
    pub fn span_note(&self, s: Span, m: &str) {
B
Brian Anderson 已提交
617 618 619
        self.tcx.sess.span_note(s, m);
    }

620 621 622 623
    pub fn span_end_note(&self, s: Span, m: &str) {
        self.tcx.sess.span_end_note(s, m);
    }

P
P1start 已提交
624 625 626 627
    pub fn span_help(&self, s: Span, m: &str) {
        self.tcx.sess.span_help(s, m);
    }

628
    pub fn bckerr_to_string(&self, err: &BckError) -> String {
B
Brian Anderson 已提交
629
        match err.code {
630
            err_mutbl => {
631
                let descr = match err.cmt.note {
632
                    mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
633
                        self.cmt_to_string(&*err.cmt)
634
                    }
635 636 637 638 639 640 641 642 643 644 645 646
                    _ => 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))
                        }
647
                    }
648 649 650
                };

                match err.cause {
651
                    euv::ClosureCapture(_) => {
A
Alex Crichton 已提交
652
                        format!("closure cannot assign to {}", descr)
653
                    }
654 655 656
                    euv::OverloadedOperator |
                    euv::AddrOf |
                    euv::RefBinding |
657
                    euv::AutoRef |
658 659
                    euv::ForLoop |
                    euv::MatchDiscriminant => {
A
Alex Crichton 已提交
660
                        format!("cannot borrow {} as mutable", descr)
661
                    }
662
                    euv::ClosureInvocation => {
663 664 665
                        self.tcx.sess.span_bug(err.span,
                            "err_mutbl with a closure invocation");
                    }
666
                }
B
Brian Anderson 已提交
667
            }
A
Alex Crichton 已提交
668
            err_out_of_scope(..) => {
669
                let msg = match opt_loan_path(&err.cmt) {
670
                    None => "borrowed value".to_string(),
671
                    Some(lp) => {
672
                        format!("`{}`", self.loan_path_to_string(&*lp))
673
                    }
674
                };
A
Alex Crichton 已提交
675
                format!("{} does not live long enough", msg)
B
Brian Anderson 已提交
676
            }
N
Niko Matsakis 已提交
677
            err_borrowed_pointer_too_short(..) => {
678
                let descr = match opt_loan_path(&err.cmt) {
679
                    Some(lp) => {
680
                        format!("`{}`", self.loan_path_to_string(&*lp))
681
                    }
682
                    None => self.cmt_to_string(&*err.cmt),
683 684
                };

A
Alex Crichton 已提交
685
                format!("lifetime of {} is too short to guarantee \
686 687
                                its contents can be safely reborrowed",
                               descr)
688
            }
N
Niko Matsakis 已提交
689 690 691
        }
    }

692
    pub fn report_aliasability_violation(&self,
693
                                         span: Span,
694 695
                                         kind: AliasableViolationKind,
                                         cause: mc::AliasableReason) {
696
        let mut is_closure = false;
N
Niko Matsakis 已提交
697
        let prefix = match kind {
698 699 700
            MutabilityViolation => {
                "cannot assign to data"
            }
701
            BorrowViolation(euv::ClosureCapture(_)) => {
702 703 704 705 706 707 708 709 710
                // 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");
            }
711 712 713
            BorrowViolation(euv::OverloadedOperator) |
            BorrowViolation(euv::AddrOf) |
            BorrowViolation(euv::AutoRef) |
714 715
            BorrowViolation(euv::RefBinding) |
            BorrowViolation(euv::MatchDiscriminant) => {
716 717
                "cannot borrow data mutably"
            }
718

719
            BorrowViolation(euv::ClosureInvocation) => {
720
                is_closure = true;
721 722
                "closure invocation"
            }
723 724 725 726

            BorrowViolation(euv::ForLoop) => {
                "`for` loop"
            }
N
Niko Matsakis 已提交
727 728 729 730 731 732
        };

        match cause {
            mc::AliasableOther => {
                self.tcx.sess.span_err(
                    span,
733
                    format!("{} in an aliasable location",
734
                             prefix).as_slice());
B
Brian Anderson 已提交
735
            }
736 737
            mc::AliasableClosure(id) => {
                self.tcx.sess.span_err(span,
738 739
                                       format!("{} in a captured outer \
                                               variable in an `Fn` closure", prefix).as_slice());
P
P1start 已提交
740
                span_help!(self.tcx.sess, self.tcx.map.span(id),
741 742
                           "consider changing this closure to take self by mutable reference");
            }
743 744
            mc::AliasableStatic(..) |
            mc::AliasableStaticMut(..) => {
745 746
                self.tcx.sess.span_err(
                    span,
747
                    format!("{} in a static location", prefix).as_slice());
748
            }
749
            mc::AliasableBorrowed => {
N
Niko Matsakis 已提交
750 751
                self.tcx.sess.span_err(
                    span,
752
                    format!("{} in a `&` reference", prefix).as_slice());
B
Brian Anderson 已提交
753 754
            }
        }
755 756

        if is_closure {
P
P1start 已提交
757
            self.tcx.sess.span_help(
758 759 760
                span,
                "closures behind references must be called via `&mut`");
        }
B
Brian Anderson 已提交
761 762
    }

763
    pub fn note_and_explain_bckerr(&self, err: BckError) {
B
Brian Anderson 已提交
764 765
        let code = err.code;
        match code {
766 767
            err_mutbl(..) => {
                match err.cmt.note {
768 769 770 771 772 773 774 775 776
                    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!()
                        };
                        if kind == ty::FnUnboxedClosureKind {
P
P1start 已提交
777
                            self.tcx.sess.span_help(
778 779 780 781
                                self.tcx.map.span(upvar_id.closure_expr_id),
                                "consider changing this closure to take \
                                 self by mutable reference");
                        }
782 783 784 785
                    }
                    _ => {}
                }
            }
B
Brian Anderson 已提交
786 787 788 789

            err_out_of_scope(super_scope, sub_scope) => {
                note_and_explain_region(
                    self.tcx,
790
                    "reference must be valid for ",
B
Brian Anderson 已提交
791
                    sub_scope,
792
                    "...");
793
                let suggestion = if is_statement_scope(self.tcx, super_scope) {
P
P1start 已提交
794
                    Some("consider using a `let` binding to increase its lifetime")
795
                } else {
P
P1start 已提交
796
                    None
797
                };
P
P1start 已提交
798
                let span = note_and_explain_region(
B
Brian Anderson 已提交
799
                    self.tcx,
800
                    "...but borrowed value is only valid for ",
B
Brian Anderson 已提交
801
                    super_scope,
P
P1start 已提交
802 803 804 805 806 807
                    "");
                match (span, suggestion) {
                    (_, None) => {},
                    (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
                    (None, Some(msg)) => self.tcx.sess.help(msg),
                }
808 809
            }

810
            err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
811
                let descr = match opt_loan_path(&err.cmt) {
812
                    Some(lp) => {
813
                        format!("`{}`", self.loan_path_to_string(&*lp))
814
                    }
815
                    None => self.cmt_to_string(&*err.cmt),
816 817 818
                };
                note_and_explain_region(
                    self.tcx,
819 820
                    format!("{} would have to be valid for ",
                            descr).as_slice(),
821 822 823 824
                    loan_scope,
                    "...");
                note_and_explain_region(
                    self.tcx,
825
                    format!("...but {} is only valid for ", descr).as_slice(),
826 827 828
                    ptr_scope,
                    "");
            }
B
Brian Anderson 已提交
829 830 831
        }
    }

832
    pub fn append_loan_path_to_string(&self,
833
                                   loan_path: &LoanPath,
834
                                   out: &mut String) {
N
Niko Matsakis 已提交
835
        match *loan_path {
836
            LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
N
Niko Matsakis 已提交
837
            LpVar(id) => {
838
                out.push_str(ty::local_var_name_str(self.tcx, id).get());
N
Niko Matsakis 已提交
839
            }
B
Brian Anderson 已提交
840

E
Eduard Burtescu 已提交
841
            LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
842
                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
843
                match fname {
844
                    mc::NamedField(fname) => {
845
                        out.push('.');
846
                        out.push_str(token::get_name(fname).get());
847 848
                    }
                    mc::PositionalField(idx) => {
849
                        out.push('.');
850
                        out.push_str(idx.to_string().as_slice());
851 852
                    }
                }
N
Niko Matsakis 已提交
853 854
            }

E
Eduard Burtescu 已提交
855
            LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
856
                self.append_autoderefd_loan_path_to_string(&**lp_base, out);
857
                out.push_str("[..]");
N
Niko Matsakis 已提交
858 859
            }

E
Eduard Burtescu 已提交
860
            LpExtend(ref lp_base, _, LpDeref(_)) => {
861
                out.push('*');
862
                self.append_loan_path_to_string(&**lp_base, out);
N
Niko Matsakis 已提交
863 864 865 866
            }
        }
    }

867
    pub fn append_autoderefd_loan_path_to_string(&self,
868
                                              loan_path: &LoanPath,
869
                                              out: &mut String) {
870
        match *loan_path {
E
Eduard Burtescu 已提交
871
            LpExtend(ref lp_base, _, LpDeref(_)) => {
872 873 874
                // 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.
875
                self.append_autoderefd_loan_path_to_string(&**lp_base, out)
876 877
            }

878
            LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
879
                self.append_loan_path_to_string(loan_path, out)
880 881 882 883
            }
        }
    }

884
    pub fn loan_path_to_string(&self, loan_path: &LoanPath) -> String {
885
        let mut result = String::new();
886
        self.append_loan_path_to_string(loan_path, &mut result);
887
        result
B
Brian Anderson 已提交
888 889
    }

890 891
    pub fn cmt_to_string(&self, cmt: &mc::cmt_) -> String {
        self.mc().cmt_to_string(cmt)
B
Brian Anderson 已提交
892
    }
N
Niko Matsakis 已提交
893 894
}

895 896 897 898 899 900 901 902 903 904 905 906
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
     }
}

907
impl BitwiseOperator for LoanDataFlowOperator {
908
    #[inline]
909 910
    fn join(&self, succ: uint, pred: uint) -> uint {
        succ | pred // loans from both preds are in scope
N
Niko Matsakis 已提交
911
    }
912
}
N
Niko Matsakis 已提交
913

914
impl DataFlowOperator for LoanDataFlowOperator {
915
    #[inline]
916 917
    fn initial_value(&self) -> bool {
        false // no loans in scope by default
N
Niko Matsakis 已提交
918 919
    }
}
920

N
Niko Matsakis 已提交
921
impl Repr for Loan {
922
    fn repr(&self, tcx: &ty::ctxt) -> String {
L
Luqman Aden 已提交
923
        format!("Loan_{}({}, {}, {}-{}, {})",
924 925 926 927 928
                 self.index,
                 self.loan_path.repr(tcx),
                 self.kind,
                 self.gen_scope,
                 self.kill_scope,
929
                 self.restricted_paths.repr(tcx))
N
Niko Matsakis 已提交
930 931 932 933
    }
}

impl Repr for LoanPath {
934
    fn repr(&self, tcx: &ty::ctxt) -> String {
N
Niko Matsakis 已提交
935 936
        match self {
            &LpVar(id) => {
937
                format!("$({})", tcx.map.node_to_string(id))
N
Niko Matsakis 已提交
938 939
            }

940
            &LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
941
                let s = tcx.map.node_to_string(var_id);
942
                format!("$({} captured by id={})", s, closure_expr_id)
943 944
            }

E
Eduard Burtescu 已提交
945
            &LpExtend(ref lp, _, LpDeref(_)) => {
946
                format!("{}.*", lp.repr(tcx))
N
Niko Matsakis 已提交
947 948
            }

E
Eduard Burtescu 已提交
949
            &LpExtend(ref lp, _, LpInterior(ref interior)) => {
950
                format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
N
Niko Matsakis 已提交
951 952
            }
        }
B
Brian Anderson 已提交
953 954
    }
}