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 The Book chapter on the borrow checker for more details.
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 self::InteriorKind::*;
23 use rustc::middle::cfg;
24 use rustc::middle::dataflow::DataFlowContext;
25 use rustc::middle::dataflow::BitwiseOperator;
26 use rustc::middle::dataflow::DataFlowOperator;
27 use rustc::middle::expr_use_visitor as euv;
28 use rustc::middle::mem_categorization as mc;
29 use rustc::middle::region;
30 use rustc::middle::ty::{self, Ty};
31 use rustc::util::ppaux::{note_and_explain_region, Repr, UserString};
33 use std::string::String;
36 use syntax::ast_map::blocks::{FnLikeNode, FnParts};
38 use syntax::codemap::Span;
39 use syntax::parse::token;
41 use syntax::visit::{Visitor, FnKind};
42 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 // A local, "cleaned" version of `mc::InteriorKind` that drops
318 // information that is not relevant to loan-path analysis. (In
319 // particular, the distinction between how precisely a array-element
320 // is tracked is irrelevant here.)
321 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
322 pub enum InteriorKind {
323 InteriorField(mc::FieldName),
324 InteriorElement(mc::ElementKind),
327 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
328 impl ToInteriorKind for mc::InteriorKind {
329 fn cleaned(self) -> InteriorKind {
331 mc::InteriorField(name) => InteriorField(name),
332 mc::InteriorElement(_, elem_kind) => InteriorElement(elem_kind),
337 #[derive(Copy, PartialEq, Eq, Hash, Debug)]
338 pub enum LoanPathElem {
339 LpDeref(mc::PointerKind), // `*LV` in doc.rs
340 LpInterior(InteriorKind), // `LV.f` in doc.rs
343 pub fn closure_to_block(closure_id: ast::NodeId,
344 tcx: &ty::ctxt) -> ast::NodeId {
345 match tcx.map.get(closure_id) {
346 ast_map::NodeExpr(expr) => match expr.node {
347 ast::ExprClosure(_, _, ref block) => {
351 panic!("encountered non-closure id: {}", closure_id)
354 _ => panic!("encountered non-expr id: {}", closure_id)
358 impl<'tcx> LoanPath<'tcx> {
359 pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
361 LpVar(local_id) => tcx.region_maps.var_scope(local_id),
362 LpUpvar(upvar_id) => {
363 let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
364 region::CodeExtent::from_node_id(block_id)
366 LpDowncast(ref base, _) |
367 LpExtend(ref base, _, _) => base.kill_scope(tcx),
371 fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
372 match (&self.kind, &other.kind) {
373 (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
375 base.has_fork(&**base2)
379 (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
380 (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
385 fn depth(&self) -> uint {
387 LpExtend(ref base, _, LpDeref(_)) => base.depth(),
388 LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
393 fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
394 match (&self.kind, &other.kind) {
395 (&LpExtend(ref base, a, LpInterior(id)),
396 &LpExtend(ref base2, _, LpInterior(id2))) => {
398 base.common(&**base2).map(|x| {
400 if base.depth() == xd && base2.depth() == xd {
401 assert_eq!(base.ty, base2.ty);
402 assert_eq!(self.ty, other.ty);
404 kind: LpExtend(Rc::new(x), a, LpInterior(id)),
412 base.common(&**base2)
415 (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
416 (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
417 (&LpVar(id), &LpVar(id2)) => {
419 assert_eq!(self.ty, other.ty);
420 Some(LoanPath { kind: LpVar(id), ty: self.ty })
425 (&LpUpvar(id), &LpUpvar(id2)) => {
427 assert_eq!(self.ty, other.ty);
428 Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
438 pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
439 //! Computes the `LoanPath` (if any) for a `cmt`.
440 //! Note that this logic is somewhat duplicated in
441 //! the method `compute()` found in `gather_loans::restrictions`,
442 //! which allows it to share common loan path pieces as it
443 //! traverses the CMT.
445 let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
449 mc::cat_static_item => {
453 mc::cat_local(id) => {
454 Some(new_lp(LpVar(id)))
457 mc::cat_upvar(mc::Upvar { id, .. }) => {
458 Some(new_lp(LpUpvar(id)))
461 mc::cat_deref(ref cmt_base, _, pk) => {
462 opt_loan_path(cmt_base).map(|lp| {
463 new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
467 mc::cat_interior(ref cmt_base, ik) => {
468 opt_loan_path(cmt_base).map(|lp| {
469 new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik.cleaned())))
473 mc::cat_downcast(ref cmt_base, variant_def_id) =>
474 opt_loan_path(cmt_base)
476 new_lp(LpDowncast(lp, variant_def_id))
482 ///////////////////////////////////////////////////////////////////////////
485 // Errors that can occur
487 pub enum bckerr_code {
489 err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
490 err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
493 // Combination of an error code and the categorization of the expression
496 pub struct BckError<'tcx> {
498 cause: euv::LoanCause,
504 pub enum AliasableViolationKind {
506 BorrowViolation(euv::LoanCause)
509 #[derive(Copy, Debug)]
510 pub enum MovedValueUseKind {
515 ///////////////////////////////////////////////////////////////////////////
518 impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
519 pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
521 self.tcx.region_maps.is_subregion_of(r_sub, r_sup)
524 pub fn report(&self, err: BckError<'tcx>) {
527 &self.bckerr_to_string(&err)[]);
528 self.note_and_explain_bckerr(err);
531 pub fn report_use_of_moved_value<'b>(&self,
533 use_kind: MovedValueUseKind,
535 the_move: &move_data::Move,
536 moved_lp: &LoanPath<'tcx>,
537 param_env: &ty::ParameterEnvironment<'b,'tcx>) {
538 let verb = match use_kind {
540 MovedInCapture => "capture",
543 let (ol, moved_lp_msg) = match the_move.kind {
544 move_data::Declared => {
545 self.tcx.sess.span_err(
547 &format!("{} of possibly uninitialized variable: `{}`",
549 self.loan_path_to_string(lp))[]);
550 (self.loan_path_to_string(moved_lp),
554 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
555 // normally generate a rather confusing message:
557 // error: use of moved value: `x.b`
558 // note: `x.a` moved here...
560 // What we want to do instead is get the 'common ancestor' of the two moves and
561 // use that for most of the message instead, giving is something like this:
563 // error: use of moved value: `x`
564 // note: `x` moved here (through moving `x.a`)...
566 let common = moved_lp.common(lp);
567 let has_common = common.is_some();
568 let has_fork = moved_lp.has_fork(lp);
569 let (nl, ol, moved_lp_msg) =
570 if has_fork && has_common {
571 let nl = self.loan_path_to_string(&common.unwrap());
573 let moved_lp_msg = format!(" (through moving `{}`)",
574 self.loan_path_to_string(moved_lp));
575 (nl, ol, moved_lp_msg)
577 (self.loan_path_to_string(lp),
578 self.loan_path_to_string(moved_lp),
582 let partial = moved_lp.depth() > lp.depth();
583 let msg = if !has_fork && partial { "partially " }
584 else if has_fork && !has_common { "collaterally "}
586 self.tcx.sess.span_err(
588 &format!("{} of {}moved value: `{}`",
596 match the_move.kind {
597 move_data::Declared => {}
599 move_data::MoveExpr => {
600 let (expr_ty, expr_span) = match self.tcx
603 Some(ast_map::NodeExpr(expr)) => {
604 (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
607 self.tcx.sess.bug(&format!("MoveExpr({}) maps to \
613 let (suggestion, _) =
614 move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
615 self.tcx.sess.span_note(
617 &format!("`{}` moved here{} because it has type `{}`, which is {}",
620 expr_ty.user_string(self.tcx),
624 move_data::MovePat => {
625 let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
626 let span = self.tcx.map.span(the_move.id);
627 self.tcx.sess.span_note(span,
628 &format!("`{}` moved here{} because it has type `{}`, \
629 which is moved by default",
632 pat_ty.user_string(self.tcx))[]);
633 self.tcx.sess.span_help(span,
634 "use `ref` to override");
637 move_data::Captured => {
638 let (expr_ty, expr_span) = match self.tcx
641 Some(ast_map::NodeExpr(expr)) => {
642 (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
645 self.tcx.sess.bug(&format!("Captured({}) maps to \
651 let (suggestion, help) =
652 move_suggestion(param_env,
656 "make a copy and capture that instead to override"));
657 self.tcx.sess.span_note(
659 &format!("`{}` moved into closure environment here{} because it \
660 has type `{}`, which is {}",
663 expr_ty.user_string(self.tcx),
665 self.tcx.sess.span_help(expr_span, help);
669 fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
672 default_msgs: (&'static str, &'static str))
673 -> (&'static str, &'static str) {
676 if ty::type_moves_by_default(param_env, span, ty) {
678 "perhaps you meant to use `clone()`?")
687 pub fn report_partial_reinitialization_of_uninitialized_structure(
690 lp: &LoanPath<'tcx>) {
694 (format!("partial reinitialization of uninitialized \
696 self.loan_path_to_string(lp))).as_slice());
699 pub fn report_reassigned_immutable_variable(&self,
703 &move_data::Assignment) {
704 self.tcx.sess.span_err(
706 &format!("re-assignment of immutable variable `{}`",
707 self.loan_path_to_string(lp))[]);
708 self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
711 pub fn span_err(&self, s: Span, m: &str) {
712 self.tcx.sess.span_err(s, m);
715 pub fn span_bug(&self, s: Span, m: &str) {
716 self.tcx.sess.span_bug(s, m);
719 pub fn span_note(&self, s: Span, m: &str) {
720 self.tcx.sess.span_note(s, m);
723 pub fn span_end_note(&self, s: Span, m: &str) {
724 self.tcx.sess.span_end_note(s, m);
727 pub fn span_help(&self, s: Span, m: &str) {
728 self.tcx.sess.span_help(s, m);
731 pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
734 let descr = match err.cmt.note {
735 mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
736 self.cmt_to_string(&*err.cmt)
738 _ => match opt_loan_path(&err.cmt) {
741 err.cmt.mutbl.to_user_str(),
742 self.cmt_to_string(&*err.cmt))
745 format!("{} {} `{}`",
746 err.cmt.mutbl.to_user_str(),
747 self.cmt_to_string(&*err.cmt),
748 self.loan_path_to_string(&*lp))
754 euv::ClosureCapture(_) => {
755 format!("closure cannot assign to {}", descr)
757 euv::OverloadedOperator |
762 euv::MatchDiscriminant => {
763 format!("cannot borrow {} as mutable", descr)
765 euv::ClosureInvocation => {
766 self.tcx.sess.span_bug(err.span,
767 "err_mutbl with a closure invocation");
771 err_out_of_scope(..) => {
772 let msg = match opt_loan_path(&err.cmt) {
773 None => "borrowed value".to_string(),
775 format!("`{}`", self.loan_path_to_string(&*lp))
778 format!("{} does not live long enough", msg)
780 err_borrowed_pointer_too_short(..) => {
781 let descr = match opt_loan_path(&err.cmt) {
783 format!("`{}`", self.loan_path_to_string(&*lp))
785 None => self.cmt_to_string(&*err.cmt),
788 format!("lifetime of {} is too short to guarantee \
789 its contents can be safely reborrowed",
795 pub fn report_aliasability_violation(&self,
797 kind: AliasableViolationKind,
798 cause: mc::AliasableReason) {
799 let mut is_closure = false;
800 let prefix = match kind {
801 MutabilityViolation => {
802 "cannot assign to data"
804 BorrowViolation(euv::ClosureCapture(_)) |
805 BorrowViolation(euv::OverloadedOperator) |
806 BorrowViolation(euv::AddrOf) |
807 BorrowViolation(euv::AutoRef) |
808 BorrowViolation(euv::RefBinding) |
809 BorrowViolation(euv::MatchDiscriminant) => {
810 "cannot borrow data mutably"
813 BorrowViolation(euv::ClosureInvocation) => {
818 BorrowViolation(euv::ForLoop) => {
824 mc::AliasableOther => {
825 self.tcx.sess.span_err(
827 &format!("{} in an aliasable location",
830 mc::AliasableClosure(id) => {
831 self.tcx.sess.span_err(span,
832 &format!("{} in a captured outer \
833 variable in an `Fn` closure", prefix));
834 if let BorrowViolation(euv::ClosureCapture(_)) = kind {
835 // The aliasability violation with closure captures can
836 // happen for nested closures, so we know the enclosing
837 // closure incorrectly accepts an `Fn` while it needs to
839 span_help!(self.tcx.sess, self.tcx.map.span(id),
840 "consider changing this to accept closures that implement `FnMut`");
842 span_help!(self.tcx.sess, self.tcx.map.span(id),
843 "consider changing this closure to take self by mutable reference");
846 mc::AliasableStatic(..) |
847 mc::AliasableStaticMut(..) => {
848 self.tcx.sess.span_err(
850 &format!("{} in a static location", prefix)[]);
852 mc::AliasableBorrowed => {
853 self.tcx.sess.span_err(
855 &format!("{} in a `&` reference", prefix)[]);
860 self.tcx.sess.span_help(
862 "closures behind references must be called via `&mut`");
866 pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
871 mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
872 // If this is an `Fn` closure, it simply can't mutate upvars.
873 // If it's an `FnMut` closure, the original variable was declared immutable.
874 // We need to determine which is the case here.
875 let kind = match err.cmt.upvar().unwrap().cat {
876 mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
879 if kind == ty::FnClosureKind {
880 self.tcx.sess.span_help(
881 self.tcx.map.span(upvar_id.closure_expr_id),
882 "consider changing this closure to take \
883 self by mutable reference");
890 err_out_of_scope(super_scope, sub_scope) => {
891 note_and_explain_region(
893 "reference must be valid for ",
896 let suggestion = if is_statement_scope(self.tcx, super_scope) {
897 Some("consider using a `let` binding to increase its lifetime")
901 let span = note_and_explain_region(
903 "...but borrowed value is only valid for ",
906 match (span, suggestion) {
908 (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
909 (None, Some(msg)) => self.tcx.sess.help(msg),
913 err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
914 let descr = match opt_loan_path(&err.cmt) {
916 format!("`{}`", self.loan_path_to_string(&*lp))
918 None => self.cmt_to_string(&*err.cmt),
920 note_and_explain_region(
922 &format!("{} would have to be valid for ",
926 note_and_explain_region(
928 &format!("...but {} is only valid for ", descr)[],
935 pub fn append_loan_path_to_string(&self,
936 loan_path: &LoanPath<'tcx>,
938 match loan_path.kind {
939 LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
941 out.push_str(&ty::local_var_name_str(self.tcx, id));
944 LpDowncast(ref lp_base, variant_def_id) => {
946 self.append_loan_path_to_string(&**lp_base, out);
947 out.push_str(DOWNCAST_PRINTED_OPERATOR);
948 out.push_str(&ty::item_path_str(self.tcx, variant_def_id)[]);
953 LpExtend(ref lp_base, _, LpInterior(InteriorField(fname))) => {
954 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
956 mc::NamedField(fname) => {
958 out.push_str(&token::get_name(fname));
960 mc::PositionalField(idx) => {
962 out.push_str(&idx.to_string()[]);
967 LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) => {
968 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
969 out.push_str("[..]");
972 LpExtend(ref lp_base, _, LpDeref(_)) => {
974 self.append_loan_path_to_string(&**lp_base, out);
979 pub fn append_autoderefd_loan_path_to_string(&self,
980 loan_path: &LoanPath<'tcx>,
982 match loan_path.kind {
983 LpExtend(ref lp_base, _, LpDeref(_)) => {
984 // For a path like `(*x).f` or `(*x)[3]`, autoderef
985 // rules would normally allow users to omit the `*x`.
986 // So just serialize such paths to `x.f` or x[3]` respectively.
987 self.append_autoderefd_loan_path_to_string(&**lp_base, out)
990 LpDowncast(ref lp_base, variant_def_id) => {
992 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
994 out.push_str(&ty::item_path_str(self.tcx, variant_def_id)[]);
998 LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
999 self.append_loan_path_to_string(loan_path, out)
1004 pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
1005 let mut result = String::new();
1006 self.append_loan_path_to_string(loan_path, &mut result);
1010 pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
1011 cmt.descriptive_string(self.tcx)
1015 fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
1017 ty::ReScope(scope) => {
1018 match tcx.map.find(scope.node_id()) {
1019 Some(ast_map::NodeStmt(_)) => true,
1027 impl BitwiseOperator for LoanDataFlowOperator {
1029 fn join(&self, succ: uint, pred: uint) -> uint {
1030 succ | pred // loans from both preds are in scope
1034 impl DataFlowOperator for LoanDataFlowOperator {
1036 fn initial_value(&self) -> bool {
1037 false // no loans in scope by default
1041 impl<'tcx> Repr<'tcx> for InteriorKind {
1042 fn repr(&self, _tcx: &ty::ctxt<'tcx>) -> String {
1044 InteriorField(mc::NamedField(fld)) =>
1045 format!("{}", token::get_name(fld)),
1046 InteriorField(mc::PositionalField(i)) => format!("#{}", i),
1047 InteriorElement(..) => "[]".to_string(),
1052 impl<'tcx> Repr<'tcx> for Loan<'tcx> {
1053 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1054 format!("Loan_{}({}, {:?}, {:?}-{:?}, {})",
1056 self.loan_path.repr(tcx),
1060 self.restricted_paths.repr(tcx))
1064 impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
1065 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1068 format!("$({})", tcx.map.node_to_string(id))
1071 LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1072 let s = tcx.map.node_to_string(var_id);
1073 format!("$({} captured by id={})", s, closure_expr_id)
1076 LpDowncast(ref lp, variant_def_id) => {
1077 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1078 ty::item_path_str(tcx, variant_def_id)
1080 variant_def_id.repr(tcx)
1082 format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1085 LpExtend(ref lp, _, LpDeref(_)) => {
1086 format!("{}.*", lp.repr(tcx))
1089 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1090 format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
1096 impl<'tcx> UserString<'tcx> for LoanPath<'tcx> {
1097 fn user_string(&self, tcx: &ty::ctxt<'tcx>) -> String {
1100 format!("$({})", tcx.map.node_to_user_string(id))
1103 LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
1104 let s = tcx.map.node_to_user_string(var_id);
1105 format!("$({} captured by closure)", s)
1108 LpDowncast(ref lp, variant_def_id) => {
1109 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1110 ty::item_path_str(tcx, variant_def_id)
1112 variant_def_id.repr(tcx)
1114 format!("({}{}{})", lp.user_string(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1117 LpExtend(ref lp, _, LpDeref(_)) => {
1118 format!("{}.*", lp.user_string(tcx))
1121 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1122 format!("{}.{}", lp.user_string(tcx), interior.repr(tcx))