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};
46 Err(e) => { return Err(e); }
59 #[derive(Clone, Copy)]
60 pub struct LoanDataFlowOperator;
62 pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
64 impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
65 fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
66 b: &'v Block, s: Span, id: ast::NodeId) {
67 borrowck_fn(self, fk, fd, b, s, id);
70 fn visit_item(&mut self, item: &ast::Item) {
71 borrowck_item(self, item);
75 pub fn check_crate(tcx: &ty::ctxt) {
76 let mut bccx = BorrowckCtxt {
86 visit::walk_crate(&mut bccx, tcx.map.krate());
88 if tcx.sess.borrowck_stats() {
89 println!("--- borrowck stats ---");
90 println!("paths requiring guarantees: {}",
91 bccx.stats.guaranteed_paths);
92 println!("paths requiring loans : {}",
93 make_stat(&bccx, bccx.stats.loaned_paths_same));
94 println!("paths requiring imm loans : {}",
95 make_stat(&bccx, bccx.stats.loaned_paths_imm));
96 println!("stable paths : {}",
97 make_stat(&bccx, bccx.stats.stable_paths));
100 fn make_stat(bccx: &BorrowckCtxt, stat: uint) -> String {
101 let total = bccx.stats.guaranteed_paths as f64;
102 let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
103 format!("{} ({:.0}%)", stat, perc)
107 fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
108 // Gather loans for items. Note that we don't need
109 // to check loans for single expressions. The check
110 // loan step is intended for things that have a data
111 // flow dependent conditions.
113 ast::ItemStatic(_, _, ref ex) |
114 ast::ItemConst(_, ref ex) => {
115 gather_loans::gather_loans_in_static_initializer(this, &**ex);
118 visit::walk_item(this, item);
123 /// Collection of conclusions determined via borrow checker analyses.
124 pub struct AnalysisData<'a, 'tcx: 'a> {
125 pub all_loans: Vec<Loan<'tcx>>,
126 pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
127 pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
130 fn borrowck_fn(this: &mut BorrowckCtxt,
136 debug!("borrowck_fn(id={})", id);
137 let cfg = cfg::CFG::new(this.tcx, body);
138 let AnalysisData { all_loans,
140 move_data:flowed_moves } =
141 build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
143 move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
146 check_loans::check_loans(this,
154 visit::walk_fn(this, fk, decl, body, sp);
157 fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
163 id: ast::NodeId) -> AnalysisData<'a, 'tcx> {
164 // Check the body of fn items.
165 let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
166 let (all_loans, move_data) =
167 gather_loans::gather_loans_in_fn(this, id, decl, body);
170 DataFlowContext::new(this.tcx,
174 LoanDataFlowOperator,
177 for (loan_idx, loan) in all_loans.iter().enumerate() {
178 loan_dfcx.add_gen(loan.gen_scope.node_id(), loan_idx);
179 loan_dfcx.add_kill(loan.kill_scope.node_id(), loan_idx);
181 loan_dfcx.add_kills_from_flow_exits(cfg);
182 loan_dfcx.propagate(cfg, body);
184 let flowed_moves = move_data::FlowedMoveData::new(move_data,
191 AnalysisData { all_loans: all_loans,
193 move_data:flowed_moves }
196 /// This and a `ty::ctxt` is all you need to run the dataflow analyses
197 /// used in the borrow checker.
198 pub struct FnPartsWithCFG<'a> {
199 pub fn_parts: FnParts<'a>,
200 pub cfg: &'a cfg::CFG,
203 impl<'a> FnPartsWithCFG<'a> {
204 pub fn from_fn_like(f: &'a FnLikeNode,
205 g: &'a cfg::CFG) -> FnPartsWithCFG<'a> {
206 FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
210 /// Accessor for introspective clients inspecting `AnalysisData` and
211 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
212 pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
213 tcx: &'a ty::ctxt<'tcx>,
214 input: FnPartsWithCFG<'a>) -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>) {
216 let mut bccx = BorrowckCtxt {
219 loaned_paths_same: 0,
226 let p = input.fn_parts;
228 let dataflow_data = build_borrowck_dataflow_data(&mut bccx,
236 (bccx, dataflow_data)
239 // ----------------------------------------------------------------------
242 pub struct BorrowckCtxt<'a, 'tcx: 'a> {
243 tcx: &'a ty::ctxt<'tcx>,
250 loaned_paths_same: uint,
251 loaned_paths_imm: uint,
253 guaranteed_paths: uint
256 pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>;
258 ///////////////////////////////////////////////////////////////////////////
259 // Loans and loan paths
261 /// Record of a loan that was issued.
262 pub struct Loan<'tcx> {
264 loan_path: Rc<LoanPath<'tcx>>,
265 kind: ty::BorrowKind,
266 restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
268 /// gen_scope indicates where loan is introduced. Typically the
269 /// loan is introduced at the point of the borrow, but in some
270 /// cases, notably method arguments, the loan may be introduced
271 /// only later, once it comes into scope. See also
272 /// `GatherLoanCtxt::compute_gen_scope`.
273 gen_scope: region::CodeExtent,
275 /// kill_scope indicates when the loan goes out of scope. This is
276 /// either when the lifetime expires or when the local variable
277 /// which roots the loan-path goes out of scope, whichever happens
278 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
279 kill_scope: region::CodeExtent,
281 cause: euv::LoanCause,
284 impl<'tcx> Loan<'tcx> {
285 pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
286 self.loan_path.clone()
290 #[derive(Eq, Hash, Show)]
291 pub struct LoanPath<'tcx> {
292 kind: LoanPathKind<'tcx>,
296 impl<'tcx> PartialEq for LoanPath<'tcx> {
297 fn eq(&self, that: &LoanPath<'tcx>) -> bool {
298 let r = self.kind == that.kind;
299 debug_assert!(self.ty == that.ty || !r,
300 "Somehow loan paths are equal though their tys are not.");
305 #[derive(PartialEq, Eq, Hash, Show)]
306 pub enum LoanPathKind<'tcx> {
307 LpVar(ast::NodeId), // `x` in doc.rs
308 LpUpvar(ty::UpvarId), // `x` captured by-value into closure
309 LpDowncast(Rc<LoanPath<'tcx>>, ast::DefId), // `x` downcast to particular enum variant
310 LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem)
313 impl<'tcx> LoanPath<'tcx> {
314 fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
315 LoanPath { kind: kind, ty: ty }
318 fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
321 // FIXME (pnkfelix): See discussion here
322 // https://github.com/pnkfelix/rust/commit/
323 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
324 static DOWNCAST_PRINTED_OPERATOR : &'static str = " as ";
326 #[derive(Copy, PartialEq, Eq, Hash, Show)]
327 pub enum LoanPathElem {
328 LpDeref(mc::PointerKind), // `*LV` in doc.rs
329 LpInterior(mc::InteriorKind) // `LV.f` in doc.rs
332 pub fn closure_to_block(closure_id: ast::NodeId,
333 tcx: &ty::ctxt) -> ast::NodeId {
334 match tcx.map.get(closure_id) {
335 ast_map::NodeExpr(expr) => match expr.node {
336 ast::ExprClosure(_, _, _, ref block) => {
340 panic!("encountered non-closure id: {}", closure_id)
343 _ => panic!("encountered non-expr id: {}", closure_id)
347 impl<'tcx> LoanPath<'tcx> {
348 pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
350 LpVar(local_id) => tcx.region_maps.var_scope(local_id),
351 LpUpvar(upvar_id) => {
352 let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
353 region::CodeExtent::from_node_id(block_id)
355 LpDowncast(ref base, _) |
356 LpExtend(ref base, _, _) => base.kill_scope(tcx),
360 fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
361 match (&self.kind, &other.kind) {
362 (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
364 base.has_fork(&**base2)
368 (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
369 (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
374 fn depth(&self) -> uint {
376 LpExtend(ref base, _, LpDeref(_)) => base.depth(),
377 LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
382 fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
383 match (&self.kind, &other.kind) {
384 (&LpExtend(ref base, a, LpInterior(id)),
385 &LpExtend(ref base2, _, LpInterior(id2))) => {
387 base.common(&**base2).map(|x| {
389 if base.depth() == xd && base2.depth() == xd {
390 assert_eq!(base.ty, base2.ty);
391 assert_eq!(self.ty, other.ty);
393 kind: LpExtend(Rc::new(x), a, LpInterior(id)),
401 base.common(&**base2)
404 (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
405 (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
406 (&LpVar(id), &LpVar(id2)) => {
408 assert_eq!(self.ty, other.ty);
409 Some(LoanPath { kind: LpVar(id), ty: self.ty })
414 (&LpUpvar(id), &LpUpvar(id2)) => {
416 assert_eq!(self.ty, other.ty);
417 Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
427 pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
428 //! Computes the `LoanPath` (if any) for a `cmt`.
429 //! Note that this logic is somewhat duplicated in
430 //! the method `compute()` found in `gather_loans::restrictions`,
431 //! which allows it to share common loan path pieces as it
432 //! traverses the CMT.
434 let new_lp = |&: v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
438 mc::cat_static_item => {
442 mc::cat_local(id) => {
443 Some(new_lp(LpVar(id)))
446 mc::cat_upvar(mc::Upvar { id, .. }) => {
447 Some(new_lp(LpUpvar(id)))
450 mc::cat_deref(ref cmt_base, _, pk) => {
451 opt_loan_path(cmt_base).map(|lp| {
452 new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
456 mc::cat_interior(ref cmt_base, ik) => {
457 opt_loan_path(cmt_base).map(|lp| {
458 new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik)))
462 mc::cat_downcast(ref cmt_base, variant_def_id) =>
463 opt_loan_path(cmt_base)
465 new_lp(LpDowncast(lp, variant_def_id))
471 ///////////////////////////////////////////////////////////////////////////
474 // Errors that can occur
476 #[allow(missing_copy_implementations)]
477 pub enum bckerr_code {
479 err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
480 err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
483 // Combination of an error code and the categorization of the expression
486 pub struct BckError<'tcx> {
488 cause: euv::LoanCause,
494 pub enum AliasableViolationKind {
496 BorrowViolation(euv::LoanCause)
499 #[derive(Copy, Show)]
500 pub enum MovedValueUseKind {
505 ///////////////////////////////////////////////////////////////////////////
508 impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
509 pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
511 self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
514 pub fn report(&self, err: BckError<'tcx>) {
517 self.bckerr_to_string(&err)[]);
518 self.note_and_explain_bckerr(err);
521 pub fn report_use_of_moved_value<'b>(&self,
523 use_kind: MovedValueUseKind,
525 the_move: &move_data::Move,
526 moved_lp: &LoanPath<'tcx>,
527 param_env: &ty::ParameterEnvironment<'b,'tcx>) {
528 let verb = match use_kind {
530 MovedInCapture => "capture",
533 let (ol, moved_lp_msg) = match the_move.kind {
534 move_data::Declared => {
535 self.tcx.sess.span_err(
537 format!("{} of possibly uninitialized variable: `{}`",
539 self.loan_path_to_string(lp))[]);
540 (self.loan_path_to_string(moved_lp),
544 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
545 // normally generate a rather confusing message:
547 // error: use of moved value: `x.b`
548 // note: `x.a` moved here...
550 // What we want to do instead is get the 'common ancestor' of the two moves and
551 // use that for most of the message instead, giving is something like this:
553 // error: use of moved value: `x`
554 // note: `x` moved here (through moving `x.a`)...
556 let common = moved_lp.common(lp);
557 let has_common = common.is_some();
558 let has_fork = moved_lp.has_fork(lp);
559 let (nl, ol, moved_lp_msg) =
560 if has_fork && has_common {
561 let nl = self.loan_path_to_string(&common.unwrap());
563 let moved_lp_msg = format!(" (through moving `{}`)",
564 self.loan_path_to_string(moved_lp));
565 (nl, ol, moved_lp_msg)
567 (self.loan_path_to_string(lp),
568 self.loan_path_to_string(moved_lp),
572 let partial = moved_lp.depth() > lp.depth();
573 let msg = if !has_fork && partial { "partially " }
574 else if has_fork && !has_common { "collaterally "}
576 self.tcx.sess.span_err(
578 format!("{} of {}moved value: `{}`",
586 match the_move.kind {
587 move_data::Declared => {}
589 move_data::MoveExpr => {
590 let (expr_ty, expr_span) = match self.tcx
593 Some(ast_map::NodeExpr(expr)) => {
594 (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
597 self.tcx.sess.bug(format!("MoveExpr({}) maps to \
603 let (suggestion, _) =
604 move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
605 self.tcx.sess.span_note(
607 format!("`{}` moved here{} because it has type `{}`, which is {}",
610 expr_ty.user_string(self.tcx),
614 move_data::MovePat => {
615 let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
616 let span = self.tcx.map.span(the_move.id);
617 self.tcx.sess.span_note(span,
618 format!("`{}` moved here{} because it has type `{}`, \
619 which is moved by default",
622 pat_ty.user_string(self.tcx))[]);
623 self.tcx.sess.span_help(span,
624 "use `ref` to override");
627 move_data::Captured => {
628 let (expr_ty, expr_span) = match self.tcx
631 Some(ast_map::NodeExpr(expr)) => {
632 (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
635 self.tcx.sess.bug(format!("Captured({}) maps to \
641 let (suggestion, help) =
642 move_suggestion(param_env,
646 "make a copy and capture that instead to override"));
647 self.tcx.sess.span_note(
649 format!("`{}` moved into closure environment here{} because it \
650 has type `{}`, which is {}",
653 expr_ty.user_string(self.tcx),
655 self.tcx.sess.span_help(expr_span, help);
659 fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
662 default_msgs: (&'static str, &'static str))
663 -> (&'static str, &'static str) {
665 ty::ty_closure(box ty::ClosureTy {
666 store: ty::RegionTraitStore(..),
669 ("a non-copyable stack closure",
670 "capture it in a new closure, e.g. `|x| f(x)`, to override")
673 if ty::type_moves_by_default(param_env, span, ty) {
675 "perhaps you meant to use `clone()`?")
684 pub fn report_reassigned_immutable_variable(&self,
688 &move_data::Assignment) {
689 self.tcx.sess.span_err(
691 format!("re-assignment of immutable variable `{}`",
692 self.loan_path_to_string(lp))[]);
693 self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
696 pub fn span_err(&self, s: Span, m: &str) {
697 self.tcx.sess.span_err(s, m);
700 pub fn span_note(&self, s: Span, m: &str) {
701 self.tcx.sess.span_note(s, m);
704 pub fn span_end_note(&self, s: Span, m: &str) {
705 self.tcx.sess.span_end_note(s, m);
708 pub fn span_help(&self, s: Span, m: &str) {
709 self.tcx.sess.span_help(s, m);
712 pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
715 let descr = match err.cmt.note {
716 mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
717 self.cmt_to_string(&*err.cmt)
719 _ => match opt_loan_path(&err.cmt) {
722 err.cmt.mutbl.to_user_str(),
723 self.cmt_to_string(&*err.cmt))
726 format!("{} {} `{}`",
727 err.cmt.mutbl.to_user_str(),
728 self.cmt_to_string(&*err.cmt),
729 self.loan_path_to_string(&*lp))
735 euv::ClosureCapture(_) => {
736 format!("closure cannot assign to {}", descr)
738 euv::OverloadedOperator |
743 euv::MatchDiscriminant => {
744 format!("cannot borrow {} as mutable", descr)
746 euv::ClosureInvocation => {
747 self.tcx.sess.span_bug(err.span,
748 "err_mutbl with a closure invocation");
752 err_out_of_scope(..) => {
753 let msg = match opt_loan_path(&err.cmt) {
754 None => "borrowed value".to_string(),
756 format!("`{}`", self.loan_path_to_string(&*lp))
759 format!("{} does not live long enough", msg)
761 err_borrowed_pointer_too_short(..) => {
762 let descr = match opt_loan_path(&err.cmt) {
764 format!("`{}`", self.loan_path_to_string(&*lp))
766 None => self.cmt_to_string(&*err.cmt),
769 format!("lifetime of {} is too short to guarantee \
770 its contents can be safely reborrowed",
776 pub fn report_aliasability_violation(&self,
778 kind: AliasableViolationKind,
779 cause: mc::AliasableReason) {
780 let mut is_closure = false;
781 let prefix = match kind {
782 MutabilityViolation => {
783 "cannot assign to data"
785 BorrowViolation(euv::ClosureCapture(_)) => {
786 // I don't think we can get aliasability violations
787 // with closure captures, so no need to come up with a
788 // good error message. The reason this cannot happen
789 // is because we only capture local variables in
790 // closures, and those are never aliasable.
791 self.tcx.sess.span_bug(
793 "aliasability violation with closure");
795 BorrowViolation(euv::OverloadedOperator) |
796 BorrowViolation(euv::AddrOf) |
797 BorrowViolation(euv::AutoRef) |
798 BorrowViolation(euv::RefBinding) |
799 BorrowViolation(euv::MatchDiscriminant) => {
800 "cannot borrow data mutably"
803 BorrowViolation(euv::ClosureInvocation) => {
808 BorrowViolation(euv::ForLoop) => {
814 mc::AliasableOther => {
815 self.tcx.sess.span_err(
817 format!("{} in an aliasable location",
820 mc::AliasableClosure(id) => {
821 self.tcx.sess.span_err(span,
822 format!("{} in a captured outer \
823 variable in an `Fn` closure", prefix)[]);
824 span_help!(self.tcx.sess, self.tcx.map.span(id),
825 "consider changing this closure to take self by mutable reference");
827 mc::AliasableStatic(..) |
828 mc::AliasableStaticMut(..) => {
829 self.tcx.sess.span_err(
831 format!("{} in a static location", prefix)[]);
833 mc::AliasableBorrowed => {
834 self.tcx.sess.span_err(
836 format!("{} in a `&` reference", prefix)[]);
841 self.tcx.sess.span_help(
843 "closures behind references must be called via `&mut`");
847 pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
852 mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
853 // If this is an `Fn` closure, it simply can't mutate upvars.
854 // If it's an `FnMut` closure, the original variable was declared immutable.
855 // We need to determine which is the case here.
856 let kind = match err.cmt.upvar().unwrap().cat {
857 mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
860 if kind == ty::FnUnboxedClosureKind {
861 self.tcx.sess.span_help(
862 self.tcx.map.span(upvar_id.closure_expr_id),
863 "consider changing this closure to take \
864 self by mutable reference");
871 err_out_of_scope(super_scope, sub_scope) => {
872 note_and_explain_region(
874 "reference must be valid for ",
877 let suggestion = if is_statement_scope(self.tcx, super_scope) {
878 Some("consider using a `let` binding to increase its lifetime")
882 let span = note_and_explain_region(
884 "...but borrowed value is only valid for ",
887 match (span, suggestion) {
889 (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
890 (None, Some(msg)) => self.tcx.sess.help(msg),
894 err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
895 let descr = match opt_loan_path(&err.cmt) {
897 format!("`{}`", self.loan_path_to_string(&*lp))
899 None => self.cmt_to_string(&*err.cmt),
901 note_and_explain_region(
903 format!("{} would have to be valid for ",
907 note_and_explain_region(
909 format!("...but {} is only valid for ", descr)[],
916 pub fn append_loan_path_to_string(&self,
917 loan_path: &LoanPath<'tcx>,
919 match loan_path.kind {
920 LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
922 out.push_str(ty::local_var_name_str(self.tcx, id).get());
925 LpDowncast(ref lp_base, variant_def_id) => {
927 self.append_loan_path_to_string(&**lp_base, out);
928 out.push_str(DOWNCAST_PRINTED_OPERATOR);
929 out.push_str(ty::item_path_str(self.tcx, variant_def_id)[]);
934 LpExtend(ref lp_base, _, LpInterior(mc::InteriorField(fname))) => {
935 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
937 mc::NamedField(fname) => {
939 out.push_str(token::get_name(fname).get());
941 mc::PositionalField(idx) => {
943 out.push_str(idx.to_string()[]);
948 LpExtend(ref lp_base, _, LpInterior(mc::InteriorElement(_))) => {
949 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
950 out.push_str("[..]");
953 LpExtend(ref lp_base, _, LpDeref(_)) => {
955 self.append_loan_path_to_string(&**lp_base, out);
960 pub fn append_autoderefd_loan_path_to_string(&self,
961 loan_path: &LoanPath<'tcx>,
963 match loan_path.kind {
964 LpExtend(ref lp_base, _, LpDeref(_)) => {
965 // For a path like `(*x).f` or `(*x)[3]`, autoderef
966 // rules would normally allow users to omit the `*x`.
967 // So just serialize such paths to `x.f` or x[3]` respectively.
968 self.append_autoderefd_loan_path_to_string(&**lp_base, out)
971 LpDowncast(ref lp_base, variant_def_id) => {
973 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
975 out.push_str(ty::item_path_str(self.tcx, variant_def_id)[]);
979 LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
980 self.append_loan_path_to_string(loan_path, out)
985 pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
986 let mut result = String::new();
987 self.append_loan_path_to_string(loan_path, &mut result);
991 pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
992 cmt.descriptive_string(self.tcx)
996 fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
998 ty::ReScope(scope) => {
999 match tcx.map.find(scope.node_id()) {
1000 Some(ast_map::NodeStmt(_)) => true,
1008 impl BitwiseOperator for LoanDataFlowOperator {
1010 fn join(&self, succ: uint, pred: uint) -> uint {
1011 succ | pred // loans from both preds are in scope
1015 impl DataFlowOperator for LoanDataFlowOperator {
1017 fn initial_value(&self) -> bool {
1018 false // no loans in scope by default
1022 impl<'tcx> Repr<'tcx> for Loan<'tcx> {
1023 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1024 format!("Loan_{}({}, {}, {}-{}, {})",
1026 self.loan_path.repr(tcx),
1030 self.restricted_paths.repr(tcx))
1034 impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
1035 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1038 format!("$({})", tcx.map.node_to_string(id))
1041 LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1042 let s = tcx.map.node_to_string(var_id);
1043 format!("$({} captured by id={})", s, closure_expr_id)
1046 LpDowncast(ref lp, variant_def_id) => {
1047 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1048 ty::item_path_str(tcx, variant_def_id)
1050 variant_def_id.repr(tcx)
1052 format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1055 LpExtend(ref lp, _, LpDeref(_)) => {
1056 format!("{}.*", lp.repr(tcx))
1059 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1060 format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
1066 impl<'tcx> UserString<'tcx> for LoanPath<'tcx> {
1067 fn user_string(&self, tcx: &ty::ctxt<'tcx>) -> String {
1070 format!("$({})", tcx.map.node_to_user_string(id))
1073 LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
1074 let s = tcx.map.node_to_user_string(var_id);
1075 format!("$({} captured by closure)", s)
1078 LpDowncast(ref lp, variant_def_id) => {
1079 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1080 ty::item_path_str(tcx, variant_def_id)
1082 variant_def_id.repr(tcx)
1084 format!("({}{}{})", lp.user_string(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1087 LpExtend(ref lp, _, LpDeref(_)) => {
1088 format!("{}.*", lp.user_string(tcx))
1091 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1092 format!("{}.{}", lp.user_string(tcx), interior.repr(tcx))