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, Show)]
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, Show)]
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, Show)]
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, Show)]
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_note(&self, s: Span, m: &str) {
685 self.tcx.sess.span_note(s, m);
688 pub fn span_end_note(&self, s: Span, m: &str) {
689 self.tcx.sess.span_end_note(s, m);
692 pub fn span_help(&self, s: Span, m: &str) {
693 self.tcx.sess.span_help(s, m);
696 pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
699 let descr = match err.cmt.note {
700 mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
701 self.cmt_to_string(&*err.cmt)
703 _ => match opt_loan_path(&err.cmt) {
706 err.cmt.mutbl.to_user_str(),
707 self.cmt_to_string(&*err.cmt))
710 format!("{} {} `{}`",
711 err.cmt.mutbl.to_user_str(),
712 self.cmt_to_string(&*err.cmt),
713 self.loan_path_to_string(&*lp))
719 euv::ClosureCapture(_) => {
720 format!("closure cannot assign to {}", descr)
722 euv::OverloadedOperator |
727 euv::MatchDiscriminant => {
728 format!("cannot borrow {} as mutable", descr)
730 euv::ClosureInvocation => {
731 self.tcx.sess.span_bug(err.span,
732 "err_mutbl with a closure invocation");
736 err_out_of_scope(..) => {
737 let msg = match opt_loan_path(&err.cmt) {
738 None => "borrowed value".to_string(),
740 format!("`{}`", self.loan_path_to_string(&*lp))
743 format!("{} does not live long enough", msg)
745 err_borrowed_pointer_too_short(..) => {
746 let descr = match opt_loan_path(&err.cmt) {
748 format!("`{}`", self.loan_path_to_string(&*lp))
750 None => self.cmt_to_string(&*err.cmt),
753 format!("lifetime of {} is too short to guarantee \
754 its contents can be safely reborrowed",
760 pub fn report_aliasability_violation(&self,
762 kind: AliasableViolationKind,
763 cause: mc::AliasableReason) {
764 let mut is_closure = false;
765 let prefix = match kind {
766 MutabilityViolation => {
767 "cannot assign to data"
769 BorrowViolation(euv::ClosureCapture(_)) => {
770 // I don't think we can get aliasability violations
771 // with closure captures, so no need to come up with a
772 // good error message. The reason this cannot happen
773 // is because we only capture local variables in
774 // closures, and those are never aliasable.
775 self.tcx.sess.span_bug(
777 "aliasability violation with closure");
779 BorrowViolation(euv::OverloadedOperator) |
780 BorrowViolation(euv::AddrOf) |
781 BorrowViolation(euv::AutoRef) |
782 BorrowViolation(euv::RefBinding) |
783 BorrowViolation(euv::MatchDiscriminant) => {
784 "cannot borrow data mutably"
787 BorrowViolation(euv::ClosureInvocation) => {
792 BorrowViolation(euv::ForLoop) => {
798 mc::AliasableOther => {
799 self.tcx.sess.span_err(
801 format!("{} in an aliasable location",
804 mc::AliasableClosure(id) => {
805 self.tcx.sess.span_err(span,
806 format!("{} in a captured outer \
807 variable in an `Fn` closure", prefix)[]);
808 span_help!(self.tcx.sess, self.tcx.map.span(id),
809 "consider changing this closure to take self by mutable reference");
811 mc::AliasableStatic(..) |
812 mc::AliasableStaticMut(..) => {
813 self.tcx.sess.span_err(
815 format!("{} in a static location", prefix)[]);
817 mc::AliasableBorrowed => {
818 self.tcx.sess.span_err(
820 format!("{} in a `&` reference", prefix)[]);
825 self.tcx.sess.span_help(
827 "closures behind references must be called via `&mut`");
831 pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
836 mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
837 // If this is an `Fn` closure, it simply can't mutate upvars.
838 // If it's an `FnMut` closure, the original variable was declared immutable.
839 // We need to determine which is the case here.
840 let kind = match err.cmt.upvar().unwrap().cat {
841 mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
844 if kind == ty::FnUnboxedClosureKind {
845 self.tcx.sess.span_help(
846 self.tcx.map.span(upvar_id.closure_expr_id),
847 "consider changing this closure to take \
848 self by mutable reference");
855 err_out_of_scope(super_scope, sub_scope) => {
856 note_and_explain_region(
858 "reference must be valid for ",
861 let suggestion = if is_statement_scope(self.tcx, super_scope) {
862 Some("consider using a `let` binding to increase its lifetime")
866 let span = note_and_explain_region(
868 "...but borrowed value is only valid for ",
871 match (span, suggestion) {
873 (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
874 (None, Some(msg)) => self.tcx.sess.help(msg),
878 err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
879 let descr = match opt_loan_path(&err.cmt) {
881 format!("`{}`", self.loan_path_to_string(&*lp))
883 None => self.cmt_to_string(&*err.cmt),
885 note_and_explain_region(
887 format!("{} would have to be valid for ",
891 note_and_explain_region(
893 format!("...but {} is only valid for ", descr)[],
900 pub fn append_loan_path_to_string(&self,
901 loan_path: &LoanPath<'tcx>,
903 match loan_path.kind {
904 LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
906 out.push_str(ty::local_var_name_str(self.tcx, id).get());
909 LpDowncast(ref lp_base, variant_def_id) => {
911 self.append_loan_path_to_string(&**lp_base, out);
912 out.push_str(DOWNCAST_PRINTED_OPERATOR);
913 out.push_str(ty::item_path_str(self.tcx, variant_def_id)[]);
918 LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
919 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
921 mc::NamedField(fname) => {
923 out.push_str(token::get_name(fname).get());
925 mc::PositionalField(idx) => {
927 out.push_str(idx.to_string()[]);
932 LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
933 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
934 out.push_str("[..]");
937 LpExtend(ref lp_base, _, LpDeref(_)) => {
939 self.append_loan_path_to_string(&**lp_base, out);
944 pub fn append_autoderefd_loan_path_to_string(&self,
945 loan_path: &LoanPath<'tcx>,
947 match loan_path.kind {
948 LpExtend(ref lp_base, _, LpDeref(_)) => {
949 // For a path like `(*x).f` or `(*x)[3]`, autoderef
950 // rules would normally allow users to omit the `*x`.
951 // So just serialize such paths to `x.f` or x[3]` respectively.
952 self.append_autoderefd_loan_path_to_string(&**lp_base, out)
955 LpDowncast(ref lp_base, variant_def_id) => {
957 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
959 out.push_str(ty::item_path_str(self.tcx, variant_def_id)[]);
963 LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
964 self.append_loan_path_to_string(loan_path, out)
969 pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
970 let mut result = String::new();
971 self.append_loan_path_to_string(loan_path, &mut result);
975 pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
976 cmt.descriptive_string(self.tcx)
980 fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
982 ty::ReScope(scope) => {
983 match tcx.map.find(scope.node_id()) {
984 Some(ast_map::NodeStmt(_)) => true,
992 impl BitwiseOperator for LoanDataFlowOperator {
994 fn join(&self, succ: uint, pred: uint) -> uint {
995 succ | pred // loans from both preds are in scope
999 impl DataFlowOperator for LoanDataFlowOperator {
1001 fn initial_value(&self) -> bool {
1002 false // no loans in scope by default
1006 impl<'tcx> Repr<'tcx> for Loan<'tcx> {
1007 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1008 format!("Loan_{}({}, {}, {}-{}, {})",
1010 self.loan_path.repr(tcx),
1014 self.restricted_paths.repr(tcx))
1018 impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
1019 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1022 format!("$({})", tcx.map.node_to_string(id))
1025 LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1026 let s = tcx.map.node_to_string(var_id);
1027 format!("$({} captured by id={})", s, closure_expr_id)
1030 LpDowncast(ref lp, variant_def_id) => {
1031 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1032 ty::item_path_str(tcx, variant_def_id)
1034 variant_def_id.repr(tcx)
1036 format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1039 LpExtend(ref lp, _, LpDeref(_)) => {
1040 format!("{}.*", lp.repr(tcx))
1043 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1044 format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
1050 impl<'tcx> UserString<'tcx> for LoanPath<'tcx> {
1051 fn user_string(&self, tcx: &ty::ctxt<'tcx>) -> String {
1054 format!("$({})", tcx.map.node_to_user_string(id))
1057 LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
1058 let s = tcx.map.node_to_user_string(var_id);
1059 format!("$({} captured by closure)", s)
1062 LpDowncast(ref lp, variant_def_id) => {
1063 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1064 ty::item_path_str(tcx, variant_def_id)
1066 variant_def_id.repr(tcx)
1068 format!("({}{}{})", lp.user_string(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1071 LpExtend(ref lp, _, LpDeref(_)) => {
1072 format!("{}.*", lp.user_string(tcx))
1075 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1076 format!("{}.{}", lp.user_string(tcx), interior.repr(tcx))