]> git.lizzy.rs Git - rust.git/blob - src/librustc_borrowck/borrowck/mod.rs
Remove unused imports
[rust.git] / src / librustc_borrowck / borrowck / mod.rs
1 // Copyright 2012-2014 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! See The Book chapter on the borrow checker for more details.
12
13 #![allow(non_camel_case_types)]
14
15 pub use self::LoanPathKind::*;
16 pub use self::LoanPathElem::*;
17 pub use self::bckerr_code::*;
18 pub use self::AliasableViolationKind::*;
19 pub use self::MovedValueUseKind::*;
20
21 use self::InteriorKind::*;
22
23 use rustc::front::map as hir_map;
24 use rustc::front::map::blocks::FnParts;
25 use rustc::middle::cfg;
26 use rustc::middle::dataflow::DataFlowContext;
27 use rustc::middle::dataflow::BitwiseOperator;
28 use rustc::middle::dataflow::DataFlowOperator;
29 use rustc::middle::dataflow::KillFrom;
30 use rustc::middle::def_id::DefId;
31 use rustc::middle::expr_use_visitor as euv;
32 use rustc::middle::free_region::FreeRegionMap;
33 use rustc::middle::mem_categorization as mc;
34 use rustc::middle::mem_categorization::Categorization;
35 use rustc::middle::region;
36 use rustc::middle::ty::{self, Ty};
37
38 use std::fmt;
39 use std::mem;
40 use std::rc::Rc;
41 use syntax::ast;
42 use syntax::codemap::Span;
43
44 use rustc_front::hir;
45 use rustc_front::hir::{FnDecl, Block};
46 use rustc_front::intravisit;
47 use rustc_front::intravisit::{Visitor, FnKind};
48 use rustc_front::util as hir_util;
49
50 pub mod check_loans;
51
52 pub mod gather_loans;
53
54 pub mod move_data;
55
56 #[derive(Clone, Copy)]
57 pub struct LoanDataFlowOperator;
58
59 pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
60
61 impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
62     fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
63                 b: &'v Block, s: Span, id: ast::NodeId) {
64         match fk {
65             FnKind::ItemFn(..) |
66             FnKind::Method(..) => {
67                 let new_free_region_map = self.tcx.free_region_map(id);
68                 let old_free_region_map =
69                     mem::replace(&mut self.free_region_map, new_free_region_map);
70                 borrowck_fn(self, fk, fd, b, s, id);
71                 self.free_region_map = old_free_region_map;
72             }
73
74             FnKind::Closure => {
75                 borrowck_fn(self, fk, fd, b, s, id);
76             }
77         }
78     }
79
80     fn visit_item(&mut self, item: &hir::Item) {
81         borrowck_item(self, item);
82     }
83
84     fn visit_trait_item(&mut self, ti: &hir::TraitItem) {
85         if let hir::ConstTraitItem(_, Some(ref expr)) = ti.node {
86             gather_loans::gather_loans_in_static_initializer(self, &*expr);
87         }
88         intravisit::walk_trait_item(self, ti);
89     }
90
91     fn visit_impl_item(&mut self, ii: &hir::ImplItem) {
92         if let hir::ImplItemKind::Const(_, ref expr) = ii.node {
93             gather_loans::gather_loans_in_static_initializer(self, &*expr);
94         }
95         intravisit::walk_impl_item(self, ii);
96     }
97 }
98
99 pub fn check_crate(tcx: &ty::ctxt) {
100     let mut bccx = BorrowckCtxt {
101         tcx: tcx,
102         free_region_map: FreeRegionMap::new(),
103         stats: BorrowStats {
104             loaned_paths_same: 0,
105             loaned_paths_imm: 0,
106             stable_paths: 0,
107             guaranteed_paths: 0
108         }
109     };
110
111     tcx.map.krate().visit_all_items(&mut bccx);
112
113     if tcx.sess.borrowck_stats() {
114         println!("--- borrowck stats ---");
115         println!("paths requiring guarantees: {}",
116                  bccx.stats.guaranteed_paths);
117         println!("paths requiring loans     : {}",
118                  make_stat(&bccx, bccx.stats.loaned_paths_same));
119         println!("paths requiring imm loans : {}",
120                  make_stat(&bccx, bccx.stats.loaned_paths_imm));
121         println!("stable paths              : {}",
122                  make_stat(&bccx, bccx.stats.stable_paths));
123     }
124
125     fn make_stat(bccx: &BorrowckCtxt, stat: usize) -> String {
126         let total = bccx.stats.guaranteed_paths as f64;
127         let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
128         format!("{} ({:.0}%)", stat, perc)
129     }
130 }
131
132 fn borrowck_item(this: &mut BorrowckCtxt, item: &hir::Item) {
133     // Gather loans for items. Note that we don't need
134     // to check loans for single expressions. The check
135     // loan step is intended for things that have a data
136     // flow dependent conditions.
137     match item.node {
138         hir::ItemStatic(_, _, ref ex) |
139         hir::ItemConst(_, ref ex) => {
140             gather_loans::gather_loans_in_static_initializer(this, &**ex);
141         }
142         _ => { }
143     }
144
145     intravisit::walk_item(this, item);
146 }
147
148 /// Collection of conclusions determined via borrow checker analyses.
149 pub struct AnalysisData<'a, 'tcx: 'a> {
150     pub all_loans: Vec<Loan<'tcx>>,
151     pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
152     pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
153 }
154
155 fn borrowck_fn(this: &mut BorrowckCtxt,
156                fk: FnKind,
157                decl: &hir::FnDecl,
158                body: &hir::Block,
159                sp: Span,
160                id: ast::NodeId) {
161     debug!("borrowck_fn(id={})", id);
162     let cfg = cfg::CFG::new(this.tcx, body);
163     let AnalysisData { all_loans,
164                        loans: loan_dfcx,
165                        move_data: flowed_moves } =
166         build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
167
168     move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
169                                                     this.tcx,
170                                                     sp,
171                                                     id);
172     move_data::fragments::build_unfragmented_map(this,
173                                                  &flowed_moves.move_data,
174                                                  id);
175
176     check_loans::check_loans(this,
177                              &loan_dfcx,
178                              &flowed_moves,
179                              &all_loans[..],
180                              id,
181                              decl,
182                              body);
183
184     intravisit::walk_fn(this, fk, decl, body, sp);
185 }
186
187 fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
188                                           fk: FnKind,
189                                           decl: &hir::FnDecl,
190                                           cfg: &cfg::CFG,
191                                           body: &hir::Block,
192                                           sp: Span,
193                                           id: ast::NodeId)
194                                           -> AnalysisData<'a, 'tcx>
195 {
196     // Check the body of fn items.
197     let tcx = this.tcx;
198     let id_range = hir_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
199     let (all_loans, move_data) =
200         gather_loans::gather_loans_in_fn(this, id, decl, body);
201
202     let mut loan_dfcx =
203         DataFlowContext::new(this.tcx,
204                              "borrowck",
205                              Some(decl),
206                              cfg,
207                              LoanDataFlowOperator,
208                              id_range,
209                              all_loans.len());
210     for (loan_idx, loan) in all_loans.iter().enumerate() {
211         loan_dfcx.add_gen(loan.gen_scope.node_id(&tcx.region_maps), loan_idx);
212         loan_dfcx.add_kill(KillFrom::ScopeEnd,
213                            loan.kill_scope.node_id(&tcx.region_maps), loan_idx);
214     }
215     loan_dfcx.add_kills_from_flow_exits(cfg);
216     loan_dfcx.propagate(cfg, body);
217
218     let flowed_moves = move_data::FlowedMoveData::new(move_data,
219                                                       this.tcx,
220                                                       cfg,
221                                                       id_range,
222                                                       decl,
223                                                       body);
224
225     AnalysisData { all_loans: all_loans,
226                    loans: loan_dfcx,
227                    move_data:flowed_moves }
228 }
229
230 /// Accessor for introspective clients inspecting `AnalysisData` and
231 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
232 pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
233     tcx: &'a ty::ctxt<'tcx>,
234     fn_parts: FnParts<'a>,
235     cfg: &cfg::CFG)
236     -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>)
237 {
238
239     let mut bccx = BorrowckCtxt {
240         tcx: tcx,
241         free_region_map: FreeRegionMap::new(),
242         stats: BorrowStats {
243             loaned_paths_same: 0,
244             loaned_paths_imm: 0,
245             stable_paths: 0,
246             guaranteed_paths: 0
247         }
248     };
249
250     let dataflow_data = build_borrowck_dataflow_data(&mut bccx,
251                                                      fn_parts.kind,
252                                                      &*fn_parts.decl,
253                                                      cfg,
254                                                      &*fn_parts.body,
255                                                      fn_parts.span,
256                                                      fn_parts.id);
257
258     (bccx, dataflow_data)
259 }
260
261 // ----------------------------------------------------------------------
262 // Type definitions
263
264 pub struct BorrowckCtxt<'a, 'tcx: 'a> {
265     tcx: &'a ty::ctxt<'tcx>,
266
267     // Hacky. As we visit various fns, we have to load up the
268     // free-region map for each one. This map is computed by during
269     // typeck for each fn item and stored -- closures just use the map
270     // from the fn item that encloses them. Since we walk the fns in
271     // order, we basically just overwrite this field as we enter a fn
272     // item and restore it afterwards in a stack-like fashion. Then
273     // the borrow checking code can assume that `free_region_map` is
274     // always the correct map for the current fn. Feels like it'd be
275     // better to just recompute this, rather than store it, but it's a
276     // bit of a pain to factor that code out at the moment.
277     free_region_map: FreeRegionMap,
278
279     // Statistics:
280     stats: BorrowStats
281 }
282
283 struct BorrowStats {
284     loaned_paths_same: usize,
285     loaned_paths_imm: usize,
286     stable_paths: usize,
287     guaranteed_paths: usize
288 }
289
290 pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>;
291
292 ///////////////////////////////////////////////////////////////////////////
293 // Loans and loan paths
294
295 /// Record of a loan that was issued.
296 pub struct Loan<'tcx> {
297     index: usize,
298     loan_path: Rc<LoanPath<'tcx>>,
299     kind: ty::BorrowKind,
300     restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
301
302     /// gen_scope indicates where loan is introduced. Typically the
303     /// loan is introduced at the point of the borrow, but in some
304     /// cases, notably method arguments, the loan may be introduced
305     /// only later, once it comes into scope.  See also
306     /// `GatherLoanCtxt::compute_gen_scope`.
307     gen_scope: region::CodeExtent,
308
309     /// kill_scope indicates when the loan goes out of scope.  This is
310     /// either when the lifetime expires or when the local variable
311     /// which roots the loan-path goes out of scope, whichever happens
312     /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
313     kill_scope: region::CodeExtent,
314     span: Span,
315     cause: euv::LoanCause,
316 }
317
318 impl<'tcx> Loan<'tcx> {
319     pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
320         self.loan_path.clone()
321     }
322 }
323
324 #[derive(Eq, Hash)]
325 pub struct LoanPath<'tcx> {
326     kind: LoanPathKind<'tcx>,
327     ty: ty::Ty<'tcx>,
328 }
329
330 impl<'tcx> PartialEq for LoanPath<'tcx> {
331     fn eq(&self, that: &LoanPath<'tcx>) -> bool {
332         let r = self.kind == that.kind;
333         debug_assert!(self.ty == that.ty || !r,
334                       "Somehow loan paths are equal though their tys are not.");
335         r
336     }
337 }
338
339 #[derive(PartialEq, Eq, Hash, Debug)]
340 pub enum LoanPathKind<'tcx> {
341     LpVar(ast::NodeId),                         // `x` in README.md
342     LpUpvar(ty::UpvarId),                       // `x` captured by-value into closure
343     LpDowncast(Rc<LoanPath<'tcx>>, DefId), // `x` downcast to particular enum variant
344     LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem)
345 }
346
347 impl<'tcx> LoanPath<'tcx> {
348     fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
349         LoanPath { kind: kind, ty: ty }
350     }
351
352     fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
353 }
354
355 // FIXME (pnkfelix): See discussion here
356 // https://github.com/pnkfelix/rust/commit/
357 //     b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
358 const DOWNCAST_PRINTED_OPERATOR: &'static str = " as ";
359
360 // A local, "cleaned" version of `mc::InteriorKind` that drops
361 // information that is not relevant to loan-path analysis. (In
362 // particular, the distinction between how precisely an array-element
363 // is tracked is irrelevant here.)
364 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
365 pub enum InteriorKind {
366     InteriorField(mc::FieldName),
367     InteriorElement(mc::ElementKind),
368 }
369
370 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
371 impl ToInteriorKind for mc::InteriorKind {
372     fn cleaned(self) -> InteriorKind {
373         match self {
374             mc::InteriorField(name) => InteriorField(name),
375             mc::InteriorElement(_, elem_kind) => InteriorElement(elem_kind),
376         }
377     }
378 }
379
380 // This can be:
381 // - a pointer dereference (`*LV` in README.md)
382 // - a field reference, with an optional definition of the containing
383 //   enum variant (`LV.f` in README.md)
384 // `DefId` is present when the field is part of struct that is in
385 // a variant of an enum. For instance in:
386 // `enum E { X { foo: u32 }, Y { foo: u32 }}`
387 // each `foo` is qualified by the definitition id of the variant (`X` or `Y`).
388 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
389 pub enum LoanPathElem {
390     LpDeref(mc::PointerKind),
391     LpInterior(Option<DefId>, InteriorKind),
392 }
393
394 pub fn closure_to_block(closure_id: ast::NodeId,
395                         tcx: &ty::ctxt) -> ast::NodeId {
396     match tcx.map.get(closure_id) {
397         hir_map::NodeExpr(expr) => match expr.node {
398             hir::ExprClosure(_, _, ref block) => {
399                 block.id
400             }
401             _ => {
402                 panic!("encountered non-closure id: {}", closure_id)
403             }
404         },
405         _ => panic!("encountered non-expr id: {}", closure_id)
406     }
407 }
408
409 impl<'tcx> LoanPath<'tcx> {
410     pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
411         match self.kind {
412             LpVar(local_id) => tcx.region_maps.var_scope(local_id),
413             LpUpvar(upvar_id) => {
414                 let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
415                 tcx.region_maps.node_extent(block_id)
416             }
417             LpDowncast(ref base, _) |
418             LpExtend(ref base, _, _) => base.kill_scope(tcx),
419         }
420     }
421
422     fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
423         match (&self.kind, &other.kind) {
424             (&LpExtend(ref base, _, LpInterior(opt_variant_id, id)),
425              &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) =>
426                 if id == id2 && opt_variant_id == opt_variant_id2 {
427                     base.has_fork(&**base2)
428                 } else {
429                     true
430                 },
431             (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
432             (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
433             _ => false,
434         }
435     }
436
437     fn depth(&self) -> usize {
438         match self.kind {
439             LpExtend(ref base, _, LpDeref(_)) => base.depth(),
440             LpExtend(ref base, _, LpInterior(_, _)) => base.depth() + 1,
441             _ => 0,
442         }
443     }
444
445     fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
446         match (&self.kind, &other.kind) {
447             (&LpExtend(ref base, a, LpInterior(opt_variant_id, id)),
448              &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) => {
449                 if id == id2 && opt_variant_id == opt_variant_id2 {
450                     base.common(&**base2).map(|x| {
451                         let xd = x.depth();
452                         if base.depth() == xd && base2.depth() == xd {
453                             assert_eq!(base.ty, base2.ty);
454                             assert_eq!(self.ty, other.ty);
455                             LoanPath {
456                                 kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)),
457                                 ty: self.ty,
458                             }
459                         } else {
460                             x
461                         }
462                     })
463                 } else {
464                     base.common(&**base2)
465                 }
466             }
467             (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
468             (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
469             (&LpVar(id), &LpVar(id2)) => {
470                 if id == id2 {
471                     assert_eq!(self.ty, other.ty);
472                     Some(LoanPath { kind: LpVar(id), ty: self.ty })
473                 } else {
474                     None
475                 }
476             }
477             (&LpUpvar(id), &LpUpvar(id2)) => {
478                 if id == id2 {
479                     assert_eq!(self.ty, other.ty);
480                     Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
481                 } else {
482                     None
483                 }
484             }
485             _ => None,
486         }
487     }
488 }
489
490 pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
491     //! Computes the `LoanPath` (if any) for a `cmt`.
492     //! Note that this logic is somewhat duplicated in
493     //! the method `compute()` found in `gather_loans::restrictions`,
494     //! which allows it to share common loan path pieces as it
495     //! traverses the CMT.
496
497     let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
498
499     match cmt.cat {
500         Categorization::Rvalue(..) |
501         Categorization::StaticItem => {
502             None
503         }
504
505         Categorization::Local(id) => {
506             Some(new_lp(LpVar(id)))
507         }
508
509         Categorization::Upvar(mc::Upvar { id, .. }) => {
510             Some(new_lp(LpUpvar(id)))
511         }
512
513         Categorization::Deref(ref cmt_base, _, pk) => {
514             opt_loan_path(cmt_base).map(|lp| {
515                 new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
516             })
517         }
518
519         Categorization::Interior(ref cmt_base, ik) => {
520             opt_loan_path(cmt_base).map(|lp| {
521                 let opt_variant_id = match cmt_base.cat {
522                     Categorization::Downcast(_, did) =>  Some(did),
523                     _ => None
524                 };
525                 new_lp(LpExtend(lp, cmt.mutbl, LpInterior(opt_variant_id, ik.cleaned())))
526             })
527         }
528
529         Categorization::Downcast(ref cmt_base, variant_def_id) =>
530             opt_loan_path(cmt_base)
531             .map(|lp| {
532                 new_lp(LpDowncast(lp, variant_def_id))
533             }),
534
535     }
536 }
537
538 ///////////////////////////////////////////////////////////////////////////
539 // Errors
540
541 // Errors that can occur
542 #[derive(PartialEq)]
543 pub enum bckerr_code {
544     err_mutbl,
545     err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
546     err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
547 }
548
549 // Combination of an error code and the categorization of the expression
550 // that caused it
551 #[derive(PartialEq)]
552 pub struct BckError<'tcx> {
553     span: Span,
554     cause: AliasableViolationKind,
555     cmt: mc::cmt<'tcx>,
556     code: bckerr_code
557 }
558
559 #[derive(Copy, Clone, Debug, PartialEq)]
560 pub enum AliasableViolationKind {
561     MutabilityViolation,
562     BorrowViolation(euv::LoanCause)
563 }
564
565 #[derive(Copy, Clone, Debug)]
566 pub enum MovedValueUseKind {
567     MovedInUse,
568     MovedInCapture,
569 }
570
571 ///////////////////////////////////////////////////////////////////////////
572 // Misc
573
574 impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
575     pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
576                            -> bool
577     {
578         self.free_region_map.is_subregion_of(self.tcx, r_sub, r_sup)
579     }
580
581     pub fn report(&self, err: BckError<'tcx>) {
582         // Catch and handle some particular cases.
583         match (&err.code, &err.cause) {
584             (&err_out_of_scope(ty::ReScope(_), ty::ReStatic),
585              &BorrowViolation(euv::ClosureCapture(span))) |
586             (&err_out_of_scope(ty::ReScope(_), ty::ReFree(..)),
587              &BorrowViolation(euv::ClosureCapture(span))) => {
588                 return self.report_out_of_scope_escaping_closure_capture(&err, span);
589             }
590             _ => { }
591         }
592
593         // General fallback.
594         self.span_err(
595             err.span,
596             &self.bckerr_to_string(&err));
597         self.note_and_explain_bckerr(err);
598     }
599
600     pub fn report_use_of_moved_value<'b>(&self,
601                                          use_span: Span,
602                                          use_kind: MovedValueUseKind,
603                                          lp: &LoanPath<'tcx>,
604                                          the_move: &move_data::Move,
605                                          moved_lp: &LoanPath<'tcx>,
606                                          param_env: &ty::ParameterEnvironment<'b,'tcx>) {
607         let verb = match use_kind {
608             MovedInUse => "use",
609             MovedInCapture => "capture",
610         };
611
612         let (ol, moved_lp_msg) = match the_move.kind {
613             move_data::Declared => {
614                 span_err!(
615                     self.tcx.sess, use_span, E0381,
616                     "{} of possibly uninitialized variable: `{}`",
617                     verb,
618                     self.loan_path_to_string(lp));
619
620                 (self.loan_path_to_string(moved_lp),
621                  String::new())
622             }
623             _ => {
624                 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
625                 // normally generate a rather confusing message:
626                 //
627                 //     error: use of moved value: `x.b`
628                 //     note: `x.a` moved here...
629                 //
630                 // What we want to do instead is get the 'common ancestor' of the two moves and
631                 // use that for most of the message instead, giving is something like this:
632                 //
633                 //     error: use of moved value: `x`
634                 //     note: `x` moved here (through moving `x.a`)...
635
636                 let common = moved_lp.common(lp);
637                 let has_common = common.is_some();
638                 let has_fork = moved_lp.has_fork(lp);
639                 let (nl, ol, moved_lp_msg) =
640                     if has_fork && has_common {
641                         let nl = self.loan_path_to_string(&common.unwrap());
642                         let ol = nl.clone();
643                         let moved_lp_msg = format!(" (through moving `{}`)",
644                                                    self.loan_path_to_string(moved_lp));
645                         (nl, ol, moved_lp_msg)
646                     } else {
647                         (self.loan_path_to_string(lp),
648                          self.loan_path_to_string(moved_lp),
649                          String::new())
650                     };
651
652                 let partial = moved_lp.depth() > lp.depth();
653                 let msg = if !has_fork && partial { "partially " }
654                           else if has_fork && !has_common { "collaterally "}
655                           else { "" };
656                 span_err!(
657                     self.tcx.sess, use_span, E0382,
658                     "{} of {}moved value: `{}`",
659                     verb, msg, nl);
660                 (ol, moved_lp_msg)
661             }
662         };
663
664         match the_move.kind {
665             move_data::Declared => {}
666
667             move_data::MoveExpr => {
668                 let (expr_ty, expr_span) = match self.tcx
669                                                      .map
670                                                      .find(the_move.id) {
671                     Some(hir_map::NodeExpr(expr)) => {
672                         (self.tcx.expr_ty_adjusted(&*expr), expr.span)
673                     }
674                     r => {
675                         self.tcx.sess.bug(&format!("MoveExpr({}) maps to \
676                                                    {:?}, not Expr",
677                                                   the_move.id,
678                                                   r))
679                     }
680                 };
681                 let (suggestion, _) =
682                     move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
683                 // If the two spans are the same, it's because the expression will be evaluated
684                 // multiple times. Avoid printing the same span and adjust the wording so it makes
685                 // more sense that it's from multiple evalutations.
686                 if expr_span == use_span {
687                     self.tcx.sess.note(
688                         &format!("`{}` was previously moved here{} because it has type `{}`, \
689                                   which is {}",
690                                  ol,
691                                  moved_lp_msg,
692                                  expr_ty,
693                                  suggestion));
694                 } else {
695                     self.tcx.sess.span_note(
696                         expr_span,
697                         &format!("`{}` moved here{} because it has type `{}`, which is {}",
698                                  ol,
699                                  moved_lp_msg,
700                                  expr_ty,
701                                  suggestion));
702                 }
703             }
704
705             move_data::MovePat => {
706                 let pat_ty = self.tcx.node_id_to_type(the_move.id);
707                 let span = self.tcx.map.span(the_move.id);
708                 self.tcx.sess.span_note(span,
709                     &format!("`{}` moved here{} because it has type `{}`, \
710                              which is moved by default",
711                             ol,
712                             moved_lp_msg,
713                             pat_ty));
714                 match self.tcx.sess.codemap().span_to_snippet(span) {
715                     Ok(string) => {
716                         self.tcx.sess.span_suggestion(
717                             span,
718                             &format!("if you would like to borrow the value instead, \
719                                       use a `ref` binding as shown:"),
720                             format!("ref {}", string));
721                     },
722                     Err(_) => {
723                         self.tcx.sess.fileline_help(span,
724                             "use `ref` to override");
725                     },
726                 }
727             }
728
729             move_data::Captured => {
730                 let (expr_ty, expr_span) = match self.tcx
731                                                      .map
732                                                      .find(the_move.id) {
733                     Some(hir_map::NodeExpr(expr)) => {
734                         (self.tcx.expr_ty_adjusted(&*expr), expr.span)
735                     }
736                     r => {
737                         self.tcx.sess.bug(&format!("Captured({}) maps to \
738                                                    {:?}, not Expr",
739                                                   the_move.id,
740                                                   r))
741                     }
742                 };
743                 let (suggestion, help) =
744                     move_suggestion(param_env,
745                                     expr_span,
746                                     expr_ty,
747                                     ("moved by default",
748                                      "make a copy and capture that instead to override"));
749                 self.tcx.sess.span_note(
750                     expr_span,
751                     &format!("`{}` moved into closure environment here{} because it \
752                             has type `{}`, which is {}",
753                             ol,
754                             moved_lp_msg,
755                             moved_lp.ty,
756                             suggestion));
757                 self.tcx.sess.fileline_help(expr_span, help);
758             }
759         }
760
761         fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
762                                     span: Span,
763                                     ty: Ty<'tcx>,
764                                     default_msgs: (&'static str, &'static str))
765                                     -> (&'static str, &'static str) {
766             match ty.sty {
767                 _ => {
768                     if ty.moves_by_default(param_env, span) {
769                         ("non-copyable",
770                          "perhaps you meant to use `clone()`?")
771                     } else {
772                         default_msgs
773                     }
774                 }
775             }
776         }
777     }
778
779     pub fn report_partial_reinitialization_of_uninitialized_structure(
780             &self,
781             span: Span,
782             lp: &LoanPath<'tcx>) {
783         span_err!(
784             self.tcx.sess, span, E0383,
785             "partial reinitialization of uninitialized structure `{}`",
786             self.loan_path_to_string(lp));
787     }
788
789     pub fn report_reassigned_immutable_variable(&self,
790                                                 span: Span,
791                                                 lp: &LoanPath<'tcx>,
792                                                 assign:
793                                                 &move_data::Assignment) {
794         span_err!(
795             self.tcx.sess, span, E0384,
796             "re-assignment of immutable variable `{}`",
797             self.loan_path_to_string(lp));
798         self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
799     }
800
801     pub fn span_err(&self, s: Span, m: &str) {
802         self.tcx.sess.span_err(s, m);
803     }
804
805     pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) {
806         self.tcx.sess.span_err_with_code(s, msg, code);
807     }
808
809     pub fn span_bug(&self, s: Span, m: &str) {
810         self.tcx.sess.span_bug(s, m);
811     }
812
813     pub fn span_note(&self, s: Span, m: &str) {
814         self.tcx.sess.span_note(s, m);
815     }
816
817     pub fn span_end_note(&self, s: Span, m: &str) {
818         self.tcx.sess.span_end_note(s, m);
819     }
820
821     pub fn fileline_help(&self, s: Span, m: &str) {
822         self.tcx.sess.fileline_help(s, m);
823     }
824
825     pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
826         match err.code {
827             err_mutbl => {
828                 let descr = match err.cmt.note {
829                     mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
830                         self.cmt_to_string(&*err.cmt)
831                     }
832                     _ => match opt_loan_path(&err.cmt) {
833                         None => {
834                             format!("{} {}",
835                                     err.cmt.mutbl.to_user_str(),
836                                     self.cmt_to_string(&*err.cmt))
837                         }
838                         Some(lp) => {
839                             format!("{} {} `{}`",
840                                     err.cmt.mutbl.to_user_str(),
841                                     self.cmt_to_string(&*err.cmt),
842                                     self.loan_path_to_string(&*lp))
843                         }
844                     }
845                 };
846
847                 match err.cause {
848                     MutabilityViolation => {
849                         format!("cannot assign to {}", descr)
850                     }
851                     BorrowViolation(euv::ClosureCapture(_)) => {
852                         format!("closure cannot assign to {}", descr)
853                     }
854                     BorrowViolation(euv::OverloadedOperator) |
855                     BorrowViolation(euv::AddrOf) |
856                     BorrowViolation(euv::RefBinding) |
857                     BorrowViolation(euv::AutoRef) |
858                     BorrowViolation(euv::AutoUnsafe) |
859                     BorrowViolation(euv::ForLoop) |
860                     BorrowViolation(euv::MatchDiscriminant) => {
861                         format!("cannot borrow {} as mutable", descr)
862                     }
863                     BorrowViolation(euv::ClosureInvocation) => {
864                         self.tcx.sess.span_bug(err.span,
865                             "err_mutbl with a closure invocation");
866                     }
867                 }
868             }
869             err_out_of_scope(..) => {
870                 let msg = match opt_loan_path(&err.cmt) {
871                     None => "borrowed value".to_string(),
872                     Some(lp) => {
873                         format!("`{}`", self.loan_path_to_string(&*lp))
874                     }
875                 };
876                 format!("{} does not live long enough", msg)
877             }
878             err_borrowed_pointer_too_short(..) => {
879                 let descr = self.cmt_to_path_or_string(&err.cmt);
880                 format!("lifetime of {} is too short to guarantee \
881                          its contents can be safely reborrowed",
882                         descr)
883             }
884         }
885     }
886
887     pub fn report_aliasability_violation(&self,
888                                          span: Span,
889                                          kind: AliasableViolationKind,
890                                          cause: mc::AliasableReason) {
891         let mut is_closure = false;
892         let prefix = match kind {
893             MutabilityViolation => {
894                 "cannot assign to data"
895             }
896             BorrowViolation(euv::ClosureCapture(_)) |
897             BorrowViolation(euv::OverloadedOperator) |
898             BorrowViolation(euv::AddrOf) |
899             BorrowViolation(euv::AutoRef) |
900             BorrowViolation(euv::AutoUnsafe) |
901             BorrowViolation(euv::RefBinding) |
902             BorrowViolation(euv::MatchDiscriminant) => {
903                 "cannot borrow data mutably"
904             }
905
906             BorrowViolation(euv::ClosureInvocation) => {
907                 is_closure = true;
908                 "closure invocation"
909             }
910
911             BorrowViolation(euv::ForLoop) => {
912                 "`for` loop"
913             }
914         };
915
916         match cause {
917             mc::AliasableOther => {
918                 span_err!(
919                     self.tcx.sess, span, E0385,
920                     "{} in an aliasable location", prefix);
921             }
922             mc::AliasableReason::UnaliasableImmutable => {
923                 span_err!(
924                     self.tcx.sess, span, E0386,
925                     "{} in an immutable container", prefix);
926             }
927             mc::AliasableClosure(id) => {
928                 span_err!(
929                     self.tcx.sess, span, E0387,
930                     "{} in a captured outer variable in an `Fn` closure", prefix);
931                 if let BorrowViolation(euv::ClosureCapture(_)) = kind {
932                     // The aliasability violation with closure captures can
933                     // happen for nested closures, so we know the enclosing
934                     // closure incorrectly accepts an `Fn` while it needs to
935                     // be `FnMut`.
936                     span_help!(self.tcx.sess, self.tcx.map.span(id),
937                            "consider changing this to accept closures that implement `FnMut`");
938                 } else {
939                     span_help!(self.tcx.sess, self.tcx.map.span(id),
940                            "consider changing this closure to take self by mutable reference");
941                 }
942             }
943             mc::AliasableStatic |
944             mc::AliasableStaticMut => {
945                 span_err!(
946                     self.tcx.sess, span, E0388,
947                     "{} in a static location", prefix);
948             }
949             mc::AliasableBorrowed => {
950                 span_err!(
951                     self.tcx.sess, span, E0389,
952                     "{} in a `&` reference", prefix);
953             }
954         }
955
956         if is_closure {
957             self.tcx.sess.fileline_help(
958                 span,
959                 "closures behind references must be called via `&mut`");
960         }
961     }
962
963     fn report_out_of_scope_escaping_closure_capture(&self,
964                                                     err: &BckError<'tcx>,
965                                                     capture_span: Span)
966     {
967         let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt);
968
969         span_err!(
970             self.tcx.sess, err.span, E0373,
971             "closure may outlive the current function, \
972              but it borrows {}, \
973              which is owned by the current function",
974             cmt_path_or_string);
975
976         self.tcx.sess.span_note(
977             capture_span,
978             &format!("{} is borrowed here",
979                      cmt_path_or_string));
980
981         let suggestion =
982             match self.tcx.sess.codemap().span_to_snippet(err.span) {
983                 Ok(string) => format!("move {}", string),
984                 Err(_) => format!("move |<args>| <body>")
985             };
986
987         self.tcx.sess.span_suggestion(
988             err.span,
989             &format!("to force the closure to take ownership of {} \
990                       (and any other referenced variables), \
991                       use the `move` keyword, as shown:",
992                      cmt_path_or_string),
993             suggestion);
994     }
995
996     pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
997         let code = err.code;
998         match code {
999             err_mutbl => {
1000                 match err.cmt.note {
1001                     mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
1002                         // If this is an `Fn` closure, it simply can't mutate upvars.
1003                         // If it's an `FnMut` closure, the original variable was declared immutable.
1004                         // We need to determine which is the case here.
1005                         let kind = match err.cmt.upvar().unwrap().cat {
1006                             Categorization::Upvar(mc::Upvar { kind, .. }) => kind,
1007                             _ => unreachable!()
1008                         };
1009                         if kind == ty::FnClosureKind {
1010                             self.tcx.sess.span_help(
1011                                 self.tcx.map.span(upvar_id.closure_expr_id),
1012                                 "consider changing this closure to take \
1013                                  self by mutable reference");
1014                         }
1015                     }
1016                     _ => {
1017                         if let Categorization::Local(local_id) = err.cmt.cat {
1018                             let span = self.tcx.map.span(local_id);
1019                             if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
1020                                 self.tcx.sess.span_suggestion(
1021                                     span,
1022                                     &format!("to make the {} mutable, use `mut` as shown:",
1023                                              self.cmt_to_string(&err.cmt)),
1024                                     format!("mut {}", snippet));
1025                             }
1026                         }
1027                     }
1028                 }
1029             }
1030
1031             err_out_of_scope(super_scope, sub_scope) => {
1032                 self.tcx.note_and_explain_region(
1033                     "reference must be valid for ",
1034                     sub_scope,
1035                     "...");
1036                 self.tcx.note_and_explain_region(
1037                     "...but borrowed value is only valid for ",
1038                     super_scope,
1039                     "");
1040                 if let Some(span) = statement_scope_span(self.tcx, super_scope) {
1041                     self.tcx.sess.span_help(span,
1042                         "consider using a `let` binding to increase its lifetime");
1043                 }
1044             }
1045
1046             err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
1047                 let descr = match opt_loan_path(&err.cmt) {
1048                     Some(lp) => {
1049                         format!("`{}`", self.loan_path_to_string(&*lp))
1050                     }
1051                     None => self.cmt_to_string(&*err.cmt),
1052                 };
1053                 self.tcx.note_and_explain_region(
1054                     &format!("{} would have to be valid for ",
1055                             descr),
1056                     loan_scope,
1057                     "...");
1058                 self.tcx.note_and_explain_region(
1059                     &format!("...but {} is only valid for ", descr),
1060                     ptr_scope,
1061                     "");
1062             }
1063         }
1064     }
1065
1066     pub fn append_loan_path_to_string(&self,
1067                                       loan_path: &LoanPath<'tcx>,
1068                                       out: &mut String) {
1069         match loan_path.kind {
1070             LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
1071             LpVar(id) => {
1072                 out.push_str(&self.tcx.local_var_name_str(id));
1073             }
1074
1075             LpDowncast(ref lp_base, variant_def_id) => {
1076                 out.push('(');
1077                 self.append_loan_path_to_string(&**lp_base, out);
1078                 out.push_str(DOWNCAST_PRINTED_OPERATOR);
1079                 out.push_str(&self.tcx.item_path_str(variant_def_id));
1080                 out.push(')');
1081             }
1082
1083
1084             LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => {
1085                 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1086                 match fname {
1087                     mc::NamedField(fname) => {
1088                         out.push('.');
1089                         out.push_str(&fname.as_str());
1090                     }
1091                     mc::PositionalField(idx) => {
1092                         out.push('.');
1093                         out.push_str(&idx.to_string());
1094                     }
1095                 }
1096             }
1097
1098             LpExtend(ref lp_base, _, LpInterior(_, InteriorElement(..))) => {
1099                 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1100                 out.push_str("[..]");
1101             }
1102
1103             LpExtend(ref lp_base, _, LpDeref(_)) => {
1104                 out.push('*');
1105                 self.append_loan_path_to_string(&**lp_base, out);
1106             }
1107         }
1108     }
1109
1110     pub fn append_autoderefd_loan_path_to_string(&self,
1111                                                  loan_path: &LoanPath<'tcx>,
1112                                                  out: &mut String) {
1113         match loan_path.kind {
1114             LpExtend(ref lp_base, _, LpDeref(_)) => {
1115                 // For a path like `(*x).f` or `(*x)[3]`, autoderef
1116                 // rules would normally allow users to omit the `*x`.
1117                 // So just serialize such paths to `x.f` or x[3]` respectively.
1118                 self.append_autoderefd_loan_path_to_string(&**lp_base, out)
1119             }
1120
1121             LpDowncast(ref lp_base, variant_def_id) => {
1122                 out.push('(');
1123                 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1124                 out.push(':');
1125                 out.push_str(&self.tcx.item_path_str(variant_def_id));
1126                 out.push(')');
1127             }
1128
1129             LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
1130                 self.append_loan_path_to_string(loan_path, out)
1131             }
1132         }
1133     }
1134
1135     pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
1136         let mut result = String::new();
1137         self.append_loan_path_to_string(loan_path, &mut result);
1138         result
1139     }
1140
1141     pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
1142         cmt.descriptive_string(self.tcx)
1143     }
1144
1145     pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String {
1146         match opt_loan_path(cmt) {
1147             Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)),
1148             None => self.cmt_to_string(cmt),
1149         }
1150     }
1151 }
1152
1153 fn statement_scope_span(tcx: &ty::ctxt, region: ty::Region) -> Option<Span> {
1154     match region {
1155         ty::ReScope(scope) => {
1156             match tcx.map.find(scope.node_id(&tcx.region_maps)) {
1157                 Some(hir_map::NodeStmt(stmt)) => Some(stmt.span),
1158                 _ => None
1159             }
1160         }
1161         _ => None
1162     }
1163 }
1164
1165 impl BitwiseOperator for LoanDataFlowOperator {
1166     #[inline]
1167     fn join(&self, succ: usize, pred: usize) -> usize {
1168         succ | pred // loans from both preds are in scope
1169     }
1170 }
1171
1172 impl DataFlowOperator for LoanDataFlowOperator {
1173     #[inline]
1174     fn initial_value(&self) -> bool {
1175         false // no loans in scope by default
1176     }
1177 }
1178
1179 impl<'tcx> fmt::Debug for InteriorKind {
1180     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1181         match *self {
1182             InteriorField(mc::NamedField(fld)) => write!(f, "{}", fld),
1183             InteriorField(mc::PositionalField(i)) => write!(f, "#{}", i),
1184             InteriorElement(..) => write!(f, "[]"),
1185         }
1186     }
1187 }
1188
1189 impl<'tcx> fmt::Debug for Loan<'tcx> {
1190     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1191         write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})",
1192                self.index,
1193                self.loan_path,
1194                self.kind,
1195                self.gen_scope,
1196                self.kill_scope,
1197                self.restricted_paths)
1198     }
1199 }
1200
1201 impl<'tcx> fmt::Debug for LoanPath<'tcx> {
1202     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1203         match self.kind {
1204             LpVar(id) => {
1205                 write!(f, "$({})", ty::tls::with(|tcx| tcx.map.node_to_string(id)))
1206             }
1207
1208             LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1209                 let s = ty::tls::with(|tcx| tcx.map.node_to_string(var_id));
1210                 write!(f, "$({} captured by id={})", s, closure_expr_id)
1211             }
1212
1213             LpDowncast(ref lp, variant_def_id) => {
1214                 let variant_str = if variant_def_id.is_local() {
1215                     ty::tls::with(|tcx| tcx.item_path_str(variant_def_id))
1216                 } else {
1217                     format!("{:?}", variant_def_id)
1218                 };
1219                 write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
1220             }
1221
1222             LpExtend(ref lp, _, LpDeref(_)) => {
1223                 write!(f, "{:?}.*", lp)
1224             }
1225
1226             LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
1227                 write!(f, "{:?}.{:?}", lp, interior)
1228             }
1229         }
1230     }
1231 }
1232
1233 impl<'tcx> fmt::Display for LoanPath<'tcx> {
1234     fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1235         match self.kind {
1236             LpVar(id) => {
1237                 write!(f, "$({})", ty::tls::with(|tcx| tcx.map.node_to_user_string(id)))
1238             }
1239
1240             LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
1241                 let s = ty::tls::with(|tcx| tcx.map.node_to_user_string(var_id));
1242                 write!(f, "$({} captured by closure)", s)
1243             }
1244
1245             LpDowncast(ref lp, variant_def_id) => {
1246                 let variant_str = if variant_def_id.is_local() {
1247                     ty::tls::with(|tcx| tcx.item_path_str(variant_def_id))
1248                 } else {
1249                     format!("{:?}", variant_def_id)
1250                 };
1251                 write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
1252             }
1253
1254             LpExtend(ref lp, _, LpDeref(_)) => {
1255                 write!(f, "{}.*", lp)
1256             }
1257
1258             LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
1259                 write!(f, "{}.{:?}", lp, interior)
1260             }
1261         }
1262     }
1263 }