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.
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.
11 //! See doc.rs for a thorough explanation of the borrow checker
13 #![allow(non_camel_case_types)]
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::*;
21 use rustc::middle::cfg;
22 use rustc::middle::dataflow::DataFlowContext;
23 use rustc::middle::dataflow::BitwiseOperator;
24 use rustc::middle::dataflow::DataFlowOperator;
25 use rustc::middle::expr_use_visitor as euv;
26 use rustc::middle::mem_categorization as mc;
27 use rustc::middle::region;
28 use rustc::middle::ty::{self, Ty};
29 use rustc::util::ppaux::{note_and_explain_region, Repr, UserString};
31 use std::string::String;
34 use syntax::ast_map::blocks::{FnLikeNode, FnParts};
36 use syntax::codemap::Span;
37 use syntax::parse::token;
39 use syntax::visit::{Visitor, FnKind};
40 use syntax::ast::{FnDecl, Block, NodeId};
50 #[derive(Clone, Copy)]
51 pub struct LoanDataFlowOperator;
53 pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
55 impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
56 fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
57 b: &'v Block, s: Span, id: ast::NodeId) {
58 borrowck_fn(self, fk, fd, b, s, id);
61 fn visit_item(&mut self, item: &ast::Item) {
62 borrowck_item(self, item);
66 pub fn check_crate(tcx: &ty::ctxt) {
67 let mut bccx = BorrowckCtxt {
77 visit::walk_crate(&mut bccx, tcx.map.krate());
79 if tcx.sess.borrowck_stats() {
80 println!("--- borrowck stats ---");
81 println!("paths requiring guarantees: {}",
82 bccx.stats.guaranteed_paths);
83 println!("paths requiring loans : {}",
84 make_stat(&bccx, bccx.stats.loaned_paths_same));
85 println!("paths requiring imm loans : {}",
86 make_stat(&bccx, bccx.stats.loaned_paths_imm));
87 println!("stable paths : {}",
88 make_stat(&bccx, bccx.stats.stable_paths));
91 fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> String {
92 let total = bccx.stats.guaranteed_paths as f64;
93 let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
94 format!("{} ({:.0}%)", stat, perc)
98 fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
99 // Gather loans for items. Note that we don't need
100 // to check loans for single expressions. The check
101 // loan step is intended for things that have a data
102 // flow dependent conditions.
104 ast::ItemStatic(_, _, ref ex) |
105 ast::ItemConst(_, ref ex) => {
106 gather_loans::gather_loans_in_static_initializer(this, &**ex);
109 visit::walk_item(this, item);
114 /// Collection of conclusions determined via borrow checker analyses.
115 pub struct AnalysisData<'a, 'tcx: 'a> {
116 pub all_loans: Vec<Loan<'tcx>>,
117 pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
118 pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
121 fn borrowck_fn(this: &mut BorrowckCtxt,
127 debug!("borrowck_fn(id={})", id);
128 let cfg = cfg::CFG::new(this.tcx, body);
129 let AnalysisData { all_loans,
131 move_data:flowed_moves } =
132 build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
134 move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
137 check_loans::check_loans(this,
145 visit::walk_fn(this, fk, decl, body, sp);
148 fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
154 id: ast::NodeId) -> AnalysisData<'a, 'tcx> {
155 // Check the body of fn items.
156 let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
157 let (all_loans, move_data) =
158 gather_loans::gather_loans_in_fn(this, id, decl, body);
161 DataFlowContext::new(this.tcx,
165 LoanDataFlowOperator,
168 for (loan_idx, loan) in all_loans.iter().enumerate() {
169 loan_dfcx.add_gen(loan.gen_scope.node_id(), loan_idx);
170 loan_dfcx.add_kill(loan.kill_scope.node_id(), loan_idx);
172 loan_dfcx.add_kills_from_flow_exits(cfg);
173 loan_dfcx.propagate(cfg, body);
175 let flowed_moves = move_data::FlowedMoveData::new(move_data,
182 AnalysisData { all_loans: all_loans,
184 move_data:flowed_moves }
187 /// This and a `ty::ctxt` is all you need to run the dataflow analyses
188 /// used in the borrow checker.
189 pub struct FnPartsWithCFG<'a> {
190 pub fn_parts: FnParts<'a>,
191 pub cfg: &'a cfg::CFG,
194 impl<'a> FnPartsWithCFG<'a> {
195 pub fn from_fn_like(f: &'a FnLikeNode,
196 g: &'a cfg::CFG) -> FnPartsWithCFG<'a> {
197 FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
201 /// Accessor for introspective clients inspecting `AnalysisData` and
202 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
203 pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
204 tcx: &'a ty::ctxt<'tcx>,
205 input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) {
207 let mut bccx = BorrowckCtxt {
210 loaned_paths_same: 0,
217 let p = input.fn_parts;
219 let dataflow_data = build_borrowck_dataflow_data(&mut bccx,
227 (bccx, dataflow_data)
230 // ----------------------------------------------------------------------
233 pub struct BorrowckCtxt<'a, 'tcx: 'a> {
234 tcx: &'a ty::ctxt<'tcx>,
241 loaned_paths_same: uint,
242 loaned_paths_imm: uint,
244 guaranteed_paths: uint
247 pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>;
249 ///////////////////////////////////////////////////////////////////////////
250 // Loans and loan paths
252 /// Record of a loan that was issued.
253 pub struct Loan<'tcx> {
255 loan_path: Rc<LoanPath<'tcx>>,
256 kind: ty::BorrowKind,
257 restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
259 /// gen_scope indicates where loan is introduced. Typically the
260 /// loan is introduced at the point of the borrow, but in some
261 /// cases, notably method arguments, the loan may be introduced
262 /// only later, once it comes into scope. See also
263 /// `GatherLoanCtxt::compute_gen_scope`.
264 gen_scope: region::CodeExtent,
266 /// kill_scope indicates when the loan goes out of scope. This is
267 /// either when the lifetime expires or when the local variable
268 /// which roots the loan-path goes out of scope, whichever happens
269 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
270 kill_scope: region::CodeExtent,
272 cause: euv::LoanCause,
275 impl<'tcx> Loan<'tcx> {
276 pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
277 self.loan_path.clone()
281 #[derive(Eq, Hash, Debug)]
282 pub struct LoanPath<'tcx> {
283 kind: LoanPathKind<'tcx>,
287 impl<'tcx> PartialEq for LoanPath<'tcx> {
288 fn eq(&self, that: &LoanPath<'tcx>) -> bool {
289 let r = self.kind == that.kind;
290 debug_assert!(self.ty == that.ty || !r,
291 "Somehow loan paths are equal though their tys are not.");
296 #[derive(PartialEq, Eq, Hash, Debug)]
297 pub enum LoanPathKind<'tcx> {
298 LpVar(ast::NodeId), // `x` in doc.rs
299 LpUpvar(ty::UpvarId), // `x` captured by-value into closure
300 LpDowncast(Rc<LoanPath<'tcx>>, ast::DefId), // `x` downcast to particular enum variant
301 LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem)
304 impl<'tcx> LoanPath<'tcx> {
305 fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
306 LoanPath { kind: kind, ty: ty }
309 fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
312 // FIXME (pnkfelix): See discussion here
313 // https://github.com/pnkfelix/rust/commit/
314 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
315 static DOWNCAST_PRINTED_OPERATOR : &'static str = " as ";
317 #[derive(Copy, PartialEq, Eq, Hash, Debug)]
318 pub enum LoanPathElem {
319 LpDeref(mc::PointerKind), // `*LV` in doc.rs
320 LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
323 pub fn closure_to_block(closure_id: ast::NodeId,
324 tcx: &ty::ctxt) -> ast::NodeId {
325 match tcx.map.get(closure_id) {
326 ast_map::NodeExpr(expr) => match expr.node {
327 ast::ExprClosure(_, _, ref block) => {
331 panic!("encountered non-closure id: {}", closure_id)
334 _ => panic!("encountered non-expr id: {}", closure_id)
338 impl<'tcx> LoanPath<'tcx> {
339 pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
341 LpVar(local_id) => tcx.region_maps.var_scope(local_id),
342 LpUpvar(upvar_id) => {
343 let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
344 region::CodeExtent::from_node_id(block_id)
346 LpDowncast(ref base, _) |
347 LpExtend(ref base, _, _) => base.kill_scope(tcx),
351 fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
352 match (&self.kind, &other.kind) {
353 (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
355 base.has_fork(&**base2)
359 (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
360 (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
365 fn depth(&self) -> uint {
367 LpExtend(ref base, _, LpDeref(_)) => base.depth(),
368 LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
373 fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
374 match (&self.kind, &other.kind) {
375 (&LpExtend(ref base, a, LpInterior(id)),
376 &LpExtend(ref base2, _, LpInterior(id2))) => {
378 base.common(&**base2).map(|x| {
380 if base.depth() == xd && base2.depth() == xd {
381 assert_eq!(base.ty, base2.ty);
382 assert_eq!(self.ty, other.ty);
384 kind: LpExtend(Rc::new(x), a, LpInterior(id)),
392 base.common(&**base2)
395 (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
396 (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
397 (&LpVar(id), &LpVar(id2)) => {
399 assert_eq!(self.ty, other.ty);
400 Some(LoanPath { kind: LpVar(id), ty: self.ty })
405 (&LpUpvar(id), &LpUpvar(id2)) => {
407 assert_eq!(self.ty, other.ty);
408 Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
418 pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
419 //! Computes the `LoanPath` (if any) for a `cmt`.
420 //! Note that this logic is somewhat duplicated in
421 //! the method `compute()` found in `gather_loans::restrictions`,
422 //! which allows it to share common loan path pieces as it
423 //! traverses the CMT.
425 let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
429 mc::cat_static_item => {
433 mc::cat_local(id) => {
434 Some(new_lp(LpVar(id)))
437 mc::cat_upvar(mc::Upvar { id, .. }) => {
438 Some(new_lp(LpUpvar(id)))
441 mc::cat_deref(ref cmt_base, _, pk) => {
442 opt_loan_path(cmt_base).map(|lp| {
443 new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
447 mc::cat_interior(ref cmt_base, ik) => {
448 opt_loan_path(cmt_base).map(|lp| {
449 new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik)))
453 mc::cat_downcast(ref cmt_base, variant_def_id) =>
454 opt_loan_path(cmt_base)
456 new_lp(LpDowncast(lp, variant_def_id))
462 ///////////////////////////////////////////////////////////////////////////
465 // Errors that can occur
467 #[allow(missing_copy_implementations)]
468 pub enum bckerr_code {
470 err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
471 err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
474 // Combination of an error code and the categorization of the expression
477 pub struct BckError<'tcx> {
479 cause: euv::LoanCause,
485 pub enum AliasableViolationKind {
487 BorrowViolation(euv::LoanCause)
490 #[derive(Copy, Debug)]
491 pub enum MovedValueUseKind {
496 ///////////////////////////////////////////////////////////////////////////
499 impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
500 pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
502 self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
505 pub fn report(&self, err: BckError<'tcx>) {
508 &self.bckerr_to_string(&err)[]);
509 self.note_and_explain_bckerr(err);
512 pub fn report_use_of_moved_value<'b>(&self,
514 use_kind: MovedValueUseKind,
516 the_move: &move_data::Move,
517 moved_lp: &LoanPath<'tcx>,
518 param_env: &ty::ParameterEnvironment<'b,'tcx>) {
519 let verb = match use_kind {
521 MovedInCapture => "capture",
524 let (ol, moved_lp_msg) = match the_move.kind {
525 move_data::Declared => {
526 self.tcx.sess.span_err(
528 &format!("{} of possibly uninitialized variable: `{}`",
530 self.loan_path_to_string(lp))[]);
531 (self.loan_path_to_string(moved_lp),
535 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
536 // normally generate a rather confusing message:
538 // error: use of moved value: `x.b`
539 // note: `x.a` moved here...
541 // What we want to do instead is get the 'common ancestor' of the two moves and
542 // use that for most of the message instead, giving is something like this:
544 // error: use of moved value: `x`
545 // note: `x` moved here (through moving `x.a`)...
547 let common = moved_lp.common(lp);
548 let has_common = common.is_some();
549 let has_fork = moved_lp.has_fork(lp);
550 let (nl, ol, moved_lp_msg) =
551 if has_fork && has_common {
552 let nl = self.loan_path_to_string(&common.unwrap());
554 let moved_lp_msg = format!(" (through moving `{}`)",
555 self.loan_path_to_string(moved_lp));
556 (nl, ol, moved_lp_msg)
558 (self.loan_path_to_string(lp),
559 self.loan_path_to_string(moved_lp),
563 let partial = moved_lp.depth() > lp.depth();
564 let msg = if !has_fork && partial { "partially " }
565 else if has_fork && !has_common { "collaterally "}
567 self.tcx.sess.span_err(
569 &format!("{} of {}moved value: `{}`",
577 match the_move.kind {
578 move_data::Declared => {}
580 move_data::MoveExpr => {
581 let (expr_ty, expr_span) = match self.tcx
584 Some(ast_map::NodeExpr(expr)) => {
585 (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
588 self.tcx.sess.bug(&format!("MoveExpr({}) maps to \
594 let (suggestion, _) =
595 move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
596 self.tcx.sess.span_note(
598 &format!("`{}` moved here{} because it has type `{}`, which is {}",
601 expr_ty.user_string(self.tcx),
605 move_data::MovePat => {
606 let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
607 let span = self.tcx.map.span(the_move.id);
608 self.tcx.sess.span_note(span,
609 &format!("`{}` moved here{} because it has type `{}`, \
610 which is moved by default",
613 pat_ty.user_string(self.tcx))[]);
614 self.tcx.sess.span_help(span,
615 "use `ref` to override");
618 move_data::Captured => {
619 let (expr_ty, expr_span) = match self.tcx
622 Some(ast_map::NodeExpr(expr)) => {
623 (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
626 self.tcx.sess.bug(&format!("Captured({}) maps to \
632 let (suggestion, help) =
633 move_suggestion(param_env,
637 "make a copy and capture that instead to override"));
638 self.tcx.sess.span_note(
640 &format!("`{}` moved into closure environment here{} because it \
641 has type `{}`, which is {}",
644 expr_ty.user_string(self.tcx),
646 self.tcx.sess.span_help(expr_span, help);
650 fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
653 default_msgs: (&'static str, &'static str))
654 -> (&'static str, &'static str) {
657 if ty::type_moves_by_default(param_env, span, ty) {
659 "perhaps you meant to use `clone()`?")
668 pub fn report_reassigned_immutable_variable(&self,
672 &move_data::Assignment) {
673 self.tcx.sess.span_err(
675 &format!("re-assignment of immutable variable `{}`",
676 self.loan_path_to_string(lp))[]);
677 self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
680 pub fn span_err(&self, s: Span, m: &str) {
681 self.tcx.sess.span_err(s, m);
684 pub fn span_bug(&self, s: Span, m: &str) {
685 self.tcx.sess.span_bug(s, m);
688 pub fn span_note(&self, s: Span, m: &str) {
689 self.tcx.sess.span_note(s, m);
692 pub fn span_end_note(&self, s: Span, m: &str) {
693 self.tcx.sess.span_end_note(s, m);
696 pub fn span_help(&self, s: Span, m: &str) {
697 self.tcx.sess.span_help(s, m);
700 pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
703 let descr = match err.cmt.note {
704 mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
705 self.cmt_to_string(&*err.cmt)
707 _ => match opt_loan_path(&err.cmt) {
710 err.cmt.mutbl.to_user_str(),
711 self.cmt_to_string(&*err.cmt))
714 format!("{} {} `{}`",
715 err.cmt.mutbl.to_user_str(),
716 self.cmt_to_string(&*err.cmt),
717 self.loan_path_to_string(&*lp))
723 euv::ClosureCapture(_) => {
724 format!("closure cannot assign to {}", descr)
726 euv::OverloadedOperator |
731 euv::MatchDiscriminant => {
732 format!("cannot borrow {} as mutable", descr)
734 euv::ClosureInvocation => {
735 self.tcx.sess.span_bug(err.span,
736 "err_mutbl with a closure invocation");
740 err_out_of_scope(..) => {
741 let msg = match opt_loan_path(&err.cmt) {
742 None => "borrowed value".to_string(),
744 format!("`{}`", self.loan_path_to_string(&*lp))
747 format!("{} does not live long enough", msg)
749 err_borrowed_pointer_too_short(..) => {
750 let descr = match opt_loan_path(&err.cmt) {
752 format!("`{}`", self.loan_path_to_string(&*lp))
754 None => self.cmt_to_string(&*err.cmt),
757 format!("lifetime of {} is too short to guarantee \
758 its contents can be safely reborrowed",
764 pub fn report_aliasability_violation(&self,
766 kind: AliasableViolationKind,
767 cause: mc::AliasableReason) {
768 let mut is_closure = false;
769 let prefix = match kind {
770 MutabilityViolation => {
771 "cannot assign to data"
773 BorrowViolation(euv::ClosureCapture(_)) |
774 BorrowViolation(euv::OverloadedOperator) |
775 BorrowViolation(euv::AddrOf) |
776 BorrowViolation(euv::AutoRef) |
777 BorrowViolation(euv::RefBinding) |
778 BorrowViolation(euv::MatchDiscriminant) => {
779 "cannot borrow data mutably"
782 BorrowViolation(euv::ClosureInvocation) => {
787 BorrowViolation(euv::ForLoop) => {
793 mc::AliasableOther => {
794 self.tcx.sess.span_err(
796 &format!("{} in an aliasable location",
799 mc::AliasableClosure(id) => {
800 self.tcx.sess.span_err(span,
801 &format!("{} in a captured outer \
802 variable in an `Fn` closure", prefix));
803 if let BorrowViolation(euv::ClosureCapture(_)) = kind {
804 // The aliasability violation with closure captures can
805 // happen for nested closures, so we know the enclosing
806 // closure incorrectly accepts an `Fn` while it needs to
808 span_help!(self.tcx.sess, self.tcx.map.span(id),
809 "consider changing this to accept closures that implement `FnMut`");
811 span_help!(self.tcx.sess, self.tcx.map.span(id),
812 "consider changing this closure to take self by mutable reference");
815 mc::AliasableStatic(..) |
816 mc::AliasableStaticMut(..) => {
817 self.tcx.sess.span_err(
819 &format!("{} in a static location", prefix)[]);
821 mc::AliasableBorrowed => {
822 self.tcx.sess.span_err(
824 &format!("{} in a `&` reference", prefix)[]);
829 self.tcx.sess.span_help(
831 "closures behind references must be called via `&mut`");
835 pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
840 mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
841 // If this is an `Fn` closure, it simply can't mutate upvars.
842 // If it's an `FnMut` closure, the original variable was declared immutable.
843 // We need to determine which is the case here.
844 let kind = match err.cmt.upvar().unwrap().cat {
845 mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
848 if kind == ty::FnClosureKind {
849 self.tcx.sess.span_help(
850 self.tcx.map.span(upvar_id.closure_expr_id),
851 "consider changing this closure to take \
852 self by mutable reference");
859 err_out_of_scope(super_scope, sub_scope) => {
860 note_and_explain_region(
862 "reference must be valid for ",
865 let suggestion = if is_statement_scope(self.tcx, super_scope) {
866 Some("consider using a `let` binding to increase its lifetime")
870 let span = note_and_explain_region(
872 "...but borrowed value is only valid for ",
875 match (span, suggestion) {
877 (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
878 (None, Some(msg)) => self.tcx.sess.help(msg),
882 err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
883 let descr = match opt_loan_path(&err.cmt) {
885 format!("`{}`", self.loan_path_to_string(&*lp))
887 None => self.cmt_to_string(&*err.cmt),
889 note_and_explain_region(
891 &format!("{} would have to be valid for ",
895 note_and_explain_region(
897 &format!("...but {} is only valid for ", descr)[],
904 pub fn append_loan_path_to_string(&self,
905 loan_path: &LoanPath<'tcx>,
907 match loan_path.kind {
908 LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
910 out.push_str(ty::local_var_name_str(self.tcx, id).get());
913 LpDowncast(ref lp_base, variant_def_id) => {
915 self.append_loan_path_to_string(&**lp_base, out);
916 out.push_str(DOWNCAST_PRINTED_OPERATOR);
917 out.push_str(&ty::item_path_str(self.tcx, variant_def_id)[]);
922 LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
923 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
925 mc::NamedField(fname) => {
927 out.push_str(token::get_name(fname).get());
929 mc::PositionalField(idx) => {
931 out.push_str(&idx.to_string()[]);
936 LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
937 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
938 out.push_str("[..]");
941 LpExtend(ref lp_base, _, LpDeref(_)) => {
943 self.append_loan_path_to_string(&**lp_base, out);
948 pub fn append_autoderefd_loan_path_to_string(&self,
949 loan_path: &LoanPath<'tcx>,
951 match loan_path.kind {
952 LpExtend(ref lp_base, _, LpDeref(_)) => {
953 // For a path like `(*x).f` or `(*x)[3]`, autoderef
954 // rules would normally allow users to omit the `*x`.
955 // So just serialize such paths to `x.f` or x[3]` respectively.
956 self.append_autoderefd_loan_path_to_string(&**lp_base, out)
959 LpDowncast(ref lp_base, variant_def_id) => {
961 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
963 out.push_str(&ty::item_path_str(self.tcx, variant_def_id)[]);
967 LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
968 self.append_loan_path_to_string(loan_path, out)
973 pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
974 let mut result = String::new();
975 self.append_loan_path_to_string(loan_path, &mut result);
979 pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
980 cmt.descriptive_string(self.tcx)
984 fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
986 ty::ReScope(scope) => {
987 match tcx.map.find(scope.node_id()) {
988 Some(ast_map::NodeStmt(_)) => true,
996 impl BitwiseOperator for LoanDataFlowOperator {
998 fn join(&self, succ: uint, pred: uint) -> uint {
999 succ | pred // loans from both preds are in scope
1003 impl DataFlowOperator for LoanDataFlowOperator {
1005 fn initial_value(&self) -> bool {
1006 false // no loans in scope by default
1010 impl<'tcx> Repr<'tcx> for Loan<'tcx> {
1011 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1012 format!("Loan_{}({}, {:?}, {:?}-{:?}, {})",
1014 self.loan_path.repr(tcx),
1018 self.restricted_paths.repr(tcx))
1022 impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
1023 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1026 format!("$({})", tcx.map.node_to_string(id))
1029 LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1030 let s = tcx.map.node_to_string(var_id);
1031 format!("$({} captured by id={})", s, closure_expr_id)
1034 LpDowncast(ref lp, variant_def_id) => {
1035 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1036 ty::item_path_str(tcx, variant_def_id)
1038 variant_def_id.repr(tcx)
1040 format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1043 LpExtend(ref lp, _, LpDeref(_)) => {
1044 format!("{}.*", lp.repr(tcx))
1047 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1048 format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
1054 impl<'tcx> UserString<'tcx> for LoanPath<'tcx> {
1055 fn user_string(&self, tcx: &ty::ctxt<'tcx>) -> String {
1058 format!("$({})", tcx.map.node_to_user_string(id))
1061 LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
1062 let s = tcx.map.node_to_user_string(var_id);
1063 format!("$({} captured by closure)", s)
1066 LpDowncast(ref lp, variant_def_id) => {
1067 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1068 ty::item_path_str(tcx, variant_def_id)
1070 variant_def_id.repr(tcx)
1072 format!("({}{}{})", lp.user_string(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1075 LpExtend(ref lp, _, LpDeref(_)) => {
1076 format!("{}.*", lp.user_string(tcx))
1079 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1080 format!("{}.{}", lp.user_string(tcx), interior.repr(tcx))