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::front::map as hir_map;
24 use rustc::front::map::blocks::FnParts;
25 use rustc::middle::cfg;
26 use rustc::middle::dataflow::DataFlowContext;
27 use rustc::middle::dataflow::BitwiseOperator;
28 use rustc::middle::dataflow::DataFlowOperator;
29 use rustc::middle::dataflow::KillFrom;
30 use rustc::middle::def_id::DefId;
31 use rustc::middle::expr_use_visitor as euv;
32 use rustc::middle::free_region::FreeRegionMap;
33 use rustc::middle::mem_categorization as mc;
34 use rustc::middle::mem_categorization::Categorization;
35 use rustc::middle::region;
36 use rustc::middle::ty::{self, Ty};
42 use syntax::codemap::Span;
45 use rustc_front::hir::{FnDecl, Block};
46 use rustc_front::intravisit;
47 use rustc_front::intravisit::{Visitor, FnKind};
48 use rustc_front::util as hir_util;
56 #[derive(Clone, Copy)]
57 pub struct LoanDataFlowOperator;
59 pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
61 impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
62 fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
63 b: &'v Block, s: Span, id: ast::NodeId) {
66 FnKind::Method(..) => {
67 let new_free_region_map = self.tcx.free_region_map(id);
68 let old_free_region_map =
69 mem::replace(&mut self.free_region_map, new_free_region_map);
70 borrowck_fn(self, fk, fd, b, s, id);
71 self.free_region_map = old_free_region_map;
75 borrowck_fn(self, fk, fd, b, s, id);
80 fn visit_item(&mut self, item: &hir::Item) {
81 borrowck_item(self, item);
84 fn visit_trait_item(&mut self, ti: &hir::TraitItem) {
85 if let hir::ConstTraitItem(_, Some(ref expr)) = ti.node {
86 gather_loans::gather_loans_in_static_initializer(self, &*expr);
88 intravisit::walk_trait_item(self, ti);
91 fn visit_impl_item(&mut self, ii: &hir::ImplItem) {
92 if let hir::ImplItemKind::Const(_, ref expr) = ii.node {
93 gather_loans::gather_loans_in_static_initializer(self, &*expr);
95 intravisit::walk_impl_item(self, ii);
99 pub fn check_crate(tcx: &ty::ctxt) {
100 let mut bccx = BorrowckCtxt {
102 free_region_map: FreeRegionMap::new(),
104 loaned_paths_same: 0,
111 tcx.map.krate().visit_all_items(&mut bccx);
113 if tcx.sess.borrowck_stats() {
114 println!("--- borrowck stats ---");
115 println!("paths requiring guarantees: {}",
116 bccx.stats.guaranteed_paths);
117 println!("paths requiring loans : {}",
118 make_stat(&bccx, bccx.stats.loaned_paths_same));
119 println!("paths requiring imm loans : {}",
120 make_stat(&bccx, bccx.stats.loaned_paths_imm));
121 println!("stable paths : {}",
122 make_stat(&bccx, bccx.stats.stable_paths));
125 fn make_stat(bccx: &BorrowckCtxt, stat: usize) -> String {
126 let total = bccx.stats.guaranteed_paths as f64;
127 let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
128 format!("{} ({:.0}%)", stat, perc)
132 fn borrowck_item(this: &mut BorrowckCtxt, item: &hir::Item) {
133 // Gather loans for items. Note that we don't need
134 // to check loans for single expressions. The check
135 // loan step is intended for things that have a data
136 // flow dependent conditions.
138 hir::ItemStatic(_, _, ref ex) |
139 hir::ItemConst(_, ref ex) => {
140 gather_loans::gather_loans_in_static_initializer(this, &**ex);
145 intravisit::walk_item(this, item);
148 /// Collection of conclusions determined via borrow checker analyses.
149 pub struct AnalysisData<'a, 'tcx: 'a> {
150 pub all_loans: Vec<Loan<'tcx>>,
151 pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
152 pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
155 fn borrowck_fn(this: &mut BorrowckCtxt,
161 debug!("borrowck_fn(id={})", id);
162 let cfg = cfg::CFG::new(this.tcx, body);
163 let AnalysisData { all_loans,
165 move_data: flowed_moves } =
166 build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
168 move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
172 move_data::fragments::build_unfragmented_map(this,
173 &flowed_moves.move_data,
176 check_loans::check_loans(this,
184 intravisit::walk_fn(this, fk, decl, body, sp);
187 fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
194 -> AnalysisData<'a, 'tcx>
196 // Check the body of fn items.
198 let id_range = hir_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
199 let (all_loans, move_data) =
200 gather_loans::gather_loans_in_fn(this, id, decl, body);
203 DataFlowContext::new(this.tcx,
207 LoanDataFlowOperator,
210 for (loan_idx, loan) in all_loans.iter().enumerate() {
211 loan_dfcx.add_gen(loan.gen_scope.node_id(&tcx.region_maps), loan_idx);
212 loan_dfcx.add_kill(KillFrom::ScopeEnd,
213 loan.kill_scope.node_id(&tcx.region_maps), loan_idx);
215 loan_dfcx.add_kills_from_flow_exits(cfg);
216 loan_dfcx.propagate(cfg, body);
218 let flowed_moves = move_data::FlowedMoveData::new(move_data,
225 AnalysisData { all_loans: all_loans,
227 move_data:flowed_moves }
230 /// Accessor for introspective clients inspecting `AnalysisData` and
231 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
232 pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
233 tcx: &'a ty::ctxt<'tcx>,
234 fn_parts: FnParts<'a>,
236 -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>)
239 let mut bccx = BorrowckCtxt {
241 free_region_map: FreeRegionMap::new(),
243 loaned_paths_same: 0,
250 let dataflow_data = build_borrowck_dataflow_data(&mut bccx,
258 (bccx, dataflow_data)
261 // ----------------------------------------------------------------------
264 pub struct BorrowckCtxt<'a, 'tcx: 'a> {
265 tcx: &'a ty::ctxt<'tcx>,
267 // Hacky. As we visit various fns, we have to load up the
268 // free-region map for each one. This map is computed by during
269 // typeck for each fn item and stored -- closures just use the map
270 // from the fn item that encloses them. Since we walk the fns in
271 // order, we basically just overwrite this field as we enter a fn
272 // item and restore it afterwards in a stack-like fashion. Then
273 // the borrow checking code can assume that `free_region_map` is
274 // always the correct map for the current fn. Feels like it'd be
275 // better to just recompute this, rather than store it, but it's a
276 // bit of a pain to factor that code out at the moment.
277 free_region_map: FreeRegionMap,
284 loaned_paths_same: usize,
285 loaned_paths_imm: usize,
287 guaranteed_paths: usize
290 pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>;
292 ///////////////////////////////////////////////////////////////////////////
293 // Loans and loan paths
295 /// Record of a loan that was issued.
296 pub struct Loan<'tcx> {
298 loan_path: Rc<LoanPath<'tcx>>,
299 kind: ty::BorrowKind,
300 restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
302 /// gen_scope indicates where loan is introduced. Typically the
303 /// loan is introduced at the point of the borrow, but in some
304 /// cases, notably method arguments, the loan may be introduced
305 /// only later, once it comes into scope. See also
306 /// `GatherLoanCtxt::compute_gen_scope`.
307 gen_scope: region::CodeExtent,
309 /// kill_scope indicates when the loan goes out of scope. This is
310 /// either when the lifetime expires or when the local variable
311 /// which roots the loan-path goes out of scope, whichever happens
312 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
313 kill_scope: region::CodeExtent,
315 cause: euv::LoanCause,
318 impl<'tcx> Loan<'tcx> {
319 pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
320 self.loan_path.clone()
325 pub struct LoanPath<'tcx> {
326 kind: LoanPathKind<'tcx>,
330 impl<'tcx> PartialEq for LoanPath<'tcx> {
331 fn eq(&self, that: &LoanPath<'tcx>) -> bool {
332 let r = self.kind == that.kind;
333 debug_assert!(self.ty == that.ty || !r,
334 "Somehow loan paths are equal though their tys are not.");
339 #[derive(PartialEq, Eq, Hash, Debug)]
340 pub enum LoanPathKind<'tcx> {
341 LpVar(ast::NodeId), // `x` in README.md
342 LpUpvar(ty::UpvarId), // `x` captured by-value into closure
343 LpDowncast(Rc<LoanPath<'tcx>>, DefId), // `x` downcast to particular enum variant
344 LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem)
347 impl<'tcx> LoanPath<'tcx> {
348 fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
349 LoanPath { kind: kind, ty: ty }
352 fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
355 // FIXME (pnkfelix): See discussion here
356 // https://github.com/pnkfelix/rust/commit/
357 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
358 const DOWNCAST_PRINTED_OPERATOR: &'static str = " as ";
360 // A local, "cleaned" version of `mc::InteriorKind` that drops
361 // information that is not relevant to loan-path analysis. (In
362 // particular, the distinction between how precisely an array-element
363 // is tracked is irrelevant here.)
364 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
365 pub enum InteriorKind {
366 InteriorField(mc::FieldName),
367 InteriorElement(mc::ElementKind),
370 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
371 impl ToInteriorKind for mc::InteriorKind {
372 fn cleaned(self) -> InteriorKind {
374 mc::InteriorField(name) => InteriorField(name),
375 mc::InteriorElement(_, elem_kind) => InteriorElement(elem_kind),
381 // - a pointer dereference (`*LV` in README.md)
382 // - a field reference, with an optional definition of the containing
383 // enum variant (`LV.f` in README.md)
384 // `DefId` is present when the field is part of struct that is in
385 // a variant of an enum. For instance in:
386 // `enum E { X { foo: u32 }, Y { foo: u32 }}`
387 // each `foo` is qualified by the definitition id of the variant (`X` or `Y`).
388 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
389 pub enum LoanPathElem {
390 LpDeref(mc::PointerKind),
391 LpInterior(Option<DefId>, InteriorKind),
394 pub fn closure_to_block(closure_id: ast::NodeId,
395 tcx: &ty::ctxt) -> ast::NodeId {
396 match tcx.map.get(closure_id) {
397 hir_map::NodeExpr(expr) => match expr.node {
398 hir::ExprClosure(_, _, ref block) => {
402 panic!("encountered non-closure id: {}", closure_id)
405 _ => panic!("encountered non-expr id: {}", closure_id)
409 impl<'tcx> LoanPath<'tcx> {
410 pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
412 LpVar(local_id) => tcx.region_maps.var_scope(local_id),
413 LpUpvar(upvar_id) => {
414 let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
415 tcx.region_maps.node_extent(block_id)
417 LpDowncast(ref base, _) |
418 LpExtend(ref base, _, _) => base.kill_scope(tcx),
422 fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
423 match (&self.kind, &other.kind) {
424 (&LpExtend(ref base, _, LpInterior(opt_variant_id, id)),
425 &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) =>
426 if id == id2 && opt_variant_id == opt_variant_id2 {
427 base.has_fork(&**base2)
431 (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
432 (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
437 fn depth(&self) -> usize {
439 LpExtend(ref base, _, LpDeref(_)) => base.depth(),
440 LpExtend(ref base, _, LpInterior(_, _)) => base.depth() + 1,
445 fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
446 match (&self.kind, &other.kind) {
447 (&LpExtend(ref base, a, LpInterior(opt_variant_id, id)),
448 &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) => {
449 if id == id2 && opt_variant_id == opt_variant_id2 {
450 base.common(&**base2).map(|x| {
452 if base.depth() == xd && base2.depth() == xd {
453 assert_eq!(base.ty, base2.ty);
454 assert_eq!(self.ty, other.ty);
456 kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)),
464 base.common(&**base2)
467 (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
468 (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
469 (&LpVar(id), &LpVar(id2)) => {
471 assert_eq!(self.ty, other.ty);
472 Some(LoanPath { kind: LpVar(id), ty: self.ty })
477 (&LpUpvar(id), &LpUpvar(id2)) => {
479 assert_eq!(self.ty, other.ty);
480 Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
490 pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
491 //! Computes the `LoanPath` (if any) for a `cmt`.
492 //! Note that this logic is somewhat duplicated in
493 //! the method `compute()` found in `gather_loans::restrictions`,
494 //! which allows it to share common loan path pieces as it
495 //! traverses the CMT.
497 let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
500 Categorization::Rvalue(..) |
501 Categorization::StaticItem => {
505 Categorization::Local(id) => {
506 Some(new_lp(LpVar(id)))
509 Categorization::Upvar(mc::Upvar { id, .. }) => {
510 Some(new_lp(LpUpvar(id)))
513 Categorization::Deref(ref cmt_base, _, pk) => {
514 opt_loan_path(cmt_base).map(|lp| {
515 new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
519 Categorization::Interior(ref cmt_base, ik) => {
520 opt_loan_path(cmt_base).map(|lp| {
521 let opt_variant_id = match cmt_base.cat {
522 Categorization::Downcast(_, did) => Some(did),
525 new_lp(LpExtend(lp, cmt.mutbl, LpInterior(opt_variant_id, ik.cleaned())))
529 Categorization::Downcast(ref cmt_base, variant_def_id) =>
530 opt_loan_path(cmt_base)
532 new_lp(LpDowncast(lp, variant_def_id))
538 ///////////////////////////////////////////////////////////////////////////
541 // Errors that can occur
543 pub enum bckerr_code {
545 err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
546 err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
549 // Combination of an error code and the categorization of the expression
552 pub struct BckError<'tcx> {
554 cause: AliasableViolationKind,
559 #[derive(Copy, Clone, Debug, PartialEq)]
560 pub enum AliasableViolationKind {
562 BorrowViolation(euv::LoanCause)
565 #[derive(Copy, Clone, Debug)]
566 pub enum MovedValueUseKind {
571 ///////////////////////////////////////////////////////////////////////////
574 impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
575 pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
578 self.free_region_map.is_subregion_of(self.tcx, r_sub, r_sup)
581 pub fn report(&self, err: BckError<'tcx>) {
582 // Catch and handle some particular cases.
583 match (&err.code, &err.cause) {
584 (&err_out_of_scope(ty::ReScope(_), ty::ReStatic),
585 &BorrowViolation(euv::ClosureCapture(span))) |
586 (&err_out_of_scope(ty::ReScope(_), ty::ReFree(..)),
587 &BorrowViolation(euv::ClosureCapture(span))) => {
588 return self.report_out_of_scope_escaping_closure_capture(&err, span);
596 &self.bckerr_to_string(&err));
597 self.note_and_explain_bckerr(err);
600 pub fn report_use_of_moved_value<'b>(&self,
602 use_kind: MovedValueUseKind,
604 the_move: &move_data::Move,
605 moved_lp: &LoanPath<'tcx>,
606 param_env: &ty::ParameterEnvironment<'b,'tcx>) {
607 let verb = match use_kind {
609 MovedInCapture => "capture",
612 let (ol, moved_lp_msg) = match the_move.kind {
613 move_data::Declared => {
615 self.tcx.sess, use_span, E0381,
616 "{} of possibly uninitialized variable: `{}`",
618 self.loan_path_to_string(lp));
620 (self.loan_path_to_string(moved_lp),
624 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
625 // normally generate a rather confusing message:
627 // error: use of moved value: `x.b`
628 // note: `x.a` moved here...
630 // What we want to do instead is get the 'common ancestor' of the two moves and
631 // use that for most of the message instead, giving is something like this:
633 // error: use of moved value: `x`
634 // note: `x` moved here (through moving `x.a`)...
636 let common = moved_lp.common(lp);
637 let has_common = common.is_some();
638 let has_fork = moved_lp.has_fork(lp);
639 let (nl, ol, moved_lp_msg) =
640 if has_fork && has_common {
641 let nl = self.loan_path_to_string(&common.unwrap());
643 let moved_lp_msg = format!(" (through moving `{}`)",
644 self.loan_path_to_string(moved_lp));
645 (nl, ol, moved_lp_msg)
647 (self.loan_path_to_string(lp),
648 self.loan_path_to_string(moved_lp),
652 let partial = moved_lp.depth() > lp.depth();
653 let msg = if !has_fork && partial { "partially " }
654 else if has_fork && !has_common { "collaterally "}
657 self.tcx.sess, use_span, E0382,
658 "{} of {}moved value: `{}`",
664 match the_move.kind {
665 move_data::Declared => {}
667 move_data::MoveExpr => {
668 let (expr_ty, expr_span) = match self.tcx
671 Some(hir_map::NodeExpr(expr)) => {
672 (self.tcx.expr_ty_adjusted(&*expr), expr.span)
675 self.tcx.sess.bug(&format!("MoveExpr({}) maps to \
681 let (suggestion, _) =
682 move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
683 // If the two spans are the same, it's because the expression will be evaluated
684 // multiple times. Avoid printing the same span and adjust the wording so it makes
685 // more sense that it's from multiple evalutations.
686 if expr_span == use_span {
688 &format!("`{}` was previously moved here{} because it has type `{}`, \
695 self.tcx.sess.span_note(
697 &format!("`{}` moved here{} because it has type `{}`, which is {}",
705 move_data::MovePat => {
706 let pat_ty = self.tcx.node_id_to_type(the_move.id);
707 let span = self.tcx.map.span(the_move.id);
708 self.tcx.sess.span_note(span,
709 &format!("`{}` moved here{} because it has type `{}`, \
710 which is moved by default",
714 match self.tcx.sess.codemap().span_to_snippet(span) {
716 self.tcx.sess.span_suggestion(
718 &format!("if you would like to borrow the value instead, \
719 use a `ref` binding as shown:"),
720 format!("ref {}", string));
723 self.tcx.sess.fileline_help(span,
724 "use `ref` to override");
729 move_data::Captured => {
730 let (expr_ty, expr_span) = match self.tcx
733 Some(hir_map::NodeExpr(expr)) => {
734 (self.tcx.expr_ty_adjusted(&*expr), expr.span)
737 self.tcx.sess.bug(&format!("Captured({}) maps to \
743 let (suggestion, help) =
744 move_suggestion(param_env,
748 "make a copy and capture that instead to override"));
749 self.tcx.sess.span_note(
751 &format!("`{}` moved into closure environment here{} because it \
752 has type `{}`, which is {}",
757 self.tcx.sess.fileline_help(expr_span, help);
761 fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
764 default_msgs: (&'static str, &'static str))
765 -> (&'static str, &'static str) {
768 if ty.moves_by_default(param_env, span) {
770 "perhaps you meant to use `clone()`?")
779 pub fn report_partial_reinitialization_of_uninitialized_structure(
782 lp: &LoanPath<'tcx>) {
784 self.tcx.sess, span, E0383,
785 "partial reinitialization of uninitialized structure `{}`",
786 self.loan_path_to_string(lp));
789 pub fn report_reassigned_immutable_variable(&self,
793 &move_data::Assignment) {
795 self.tcx.sess, span, E0384,
796 "re-assignment of immutable variable `{}`",
797 self.loan_path_to_string(lp));
798 self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
801 pub fn span_err(&self, s: Span, m: &str) {
802 self.tcx.sess.span_err(s, m);
805 pub fn span_err_with_code(&self, s: Span, msg: &str, code: &str) {
806 self.tcx.sess.span_err_with_code(s, msg, code);
809 pub fn span_bug(&self, s: Span, m: &str) {
810 self.tcx.sess.span_bug(s, m);
813 pub fn span_note(&self, s: Span, m: &str) {
814 self.tcx.sess.span_note(s, m);
817 pub fn span_end_note(&self, s: Span, m: &str) {
818 self.tcx.sess.span_end_note(s, m);
821 pub fn fileline_help(&self, s: Span, m: &str) {
822 self.tcx.sess.fileline_help(s, m);
825 pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
828 let descr = match err.cmt.note {
829 mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
830 self.cmt_to_string(&*err.cmt)
832 _ => match opt_loan_path(&err.cmt) {
835 err.cmt.mutbl.to_user_str(),
836 self.cmt_to_string(&*err.cmt))
839 format!("{} {} `{}`",
840 err.cmt.mutbl.to_user_str(),
841 self.cmt_to_string(&*err.cmt),
842 self.loan_path_to_string(&*lp))
848 MutabilityViolation => {
849 format!("cannot assign to {}", descr)
851 BorrowViolation(euv::ClosureCapture(_)) => {
852 format!("closure cannot assign to {}", descr)
854 BorrowViolation(euv::OverloadedOperator) |
855 BorrowViolation(euv::AddrOf) |
856 BorrowViolation(euv::RefBinding) |
857 BorrowViolation(euv::AutoRef) |
858 BorrowViolation(euv::AutoUnsafe) |
859 BorrowViolation(euv::ForLoop) |
860 BorrowViolation(euv::MatchDiscriminant) => {
861 format!("cannot borrow {} as mutable", descr)
863 BorrowViolation(euv::ClosureInvocation) => {
864 self.tcx.sess.span_bug(err.span,
865 "err_mutbl with a closure invocation");
869 err_out_of_scope(..) => {
870 let msg = match opt_loan_path(&err.cmt) {
871 None => "borrowed value".to_string(),
873 format!("`{}`", self.loan_path_to_string(&*lp))
876 format!("{} does not live long enough", msg)
878 err_borrowed_pointer_too_short(..) => {
879 let descr = self.cmt_to_path_or_string(&err.cmt);
880 format!("lifetime of {} is too short to guarantee \
881 its contents can be safely reborrowed",
887 pub fn report_aliasability_violation(&self,
889 kind: AliasableViolationKind,
890 cause: mc::AliasableReason) {
891 let mut is_closure = false;
892 let prefix = match kind {
893 MutabilityViolation => {
894 "cannot assign to data"
896 BorrowViolation(euv::ClosureCapture(_)) |
897 BorrowViolation(euv::OverloadedOperator) |
898 BorrowViolation(euv::AddrOf) |
899 BorrowViolation(euv::AutoRef) |
900 BorrowViolation(euv::AutoUnsafe) |
901 BorrowViolation(euv::RefBinding) |
902 BorrowViolation(euv::MatchDiscriminant) => {
903 "cannot borrow data mutably"
906 BorrowViolation(euv::ClosureInvocation) => {
911 BorrowViolation(euv::ForLoop) => {
917 mc::AliasableOther => {
919 self.tcx.sess, span, E0385,
920 "{} in an aliasable location", prefix);
922 mc::AliasableReason::UnaliasableImmutable => {
924 self.tcx.sess, span, E0386,
925 "{} in an immutable container", prefix);
927 mc::AliasableClosure(id) => {
929 self.tcx.sess, span, E0387,
930 "{} in a captured outer variable in an `Fn` closure", prefix);
931 if let BorrowViolation(euv::ClosureCapture(_)) = kind {
932 // The aliasability violation with closure captures can
933 // happen for nested closures, so we know the enclosing
934 // closure incorrectly accepts an `Fn` while it needs to
936 span_help!(self.tcx.sess, self.tcx.map.span(id),
937 "consider changing this to accept closures that implement `FnMut`");
939 span_help!(self.tcx.sess, self.tcx.map.span(id),
940 "consider changing this closure to take self by mutable reference");
943 mc::AliasableStatic |
944 mc::AliasableStaticMut => {
946 self.tcx.sess, span, E0388,
947 "{} in a static location", prefix);
949 mc::AliasableBorrowed => {
951 self.tcx.sess, span, E0389,
952 "{} in a `&` reference", prefix);
957 self.tcx.sess.fileline_help(
959 "closures behind references must be called via `&mut`");
963 fn report_out_of_scope_escaping_closure_capture(&self,
964 err: &BckError<'tcx>,
967 let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt);
970 self.tcx.sess, err.span, E0373,
971 "closure may outlive the current function, \
973 which is owned by the current function",
976 self.tcx.sess.span_note(
978 &format!("{} is borrowed here",
979 cmt_path_or_string));
982 match self.tcx.sess.codemap().span_to_snippet(err.span) {
983 Ok(string) => format!("move {}", string),
984 Err(_) => format!("move |<args>| <body>")
987 self.tcx.sess.span_suggestion(
989 &format!("to force the closure to take ownership of {} \
990 (and any other referenced variables), \
991 use the `move` keyword, as shown:",
996 pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
1000 match err.cmt.note {
1001 mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
1002 // If this is an `Fn` closure, it simply can't mutate upvars.
1003 // If it's an `FnMut` closure, the original variable was declared immutable.
1004 // We need to determine which is the case here.
1005 let kind = match err.cmt.upvar().unwrap().cat {
1006 Categorization::Upvar(mc::Upvar { kind, .. }) => kind,
1009 if kind == ty::FnClosureKind {
1010 self.tcx.sess.span_help(
1011 self.tcx.map.span(upvar_id.closure_expr_id),
1012 "consider changing this closure to take \
1013 self by mutable reference");
1017 if let Categorization::Local(local_id) = err.cmt.cat {
1018 let span = self.tcx.map.span(local_id);
1019 if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
1020 self.tcx.sess.span_suggestion(
1022 &format!("to make the {} mutable, use `mut` as shown:",
1023 self.cmt_to_string(&err.cmt)),
1024 format!("mut {}", snippet));
1031 err_out_of_scope(super_scope, sub_scope) => {
1032 self.tcx.note_and_explain_region(
1033 "reference must be valid for ",
1036 self.tcx.note_and_explain_region(
1037 "...but borrowed value is only valid for ",
1040 if let Some(span) = statement_scope_span(self.tcx, super_scope) {
1041 self.tcx.sess.span_help(span,
1042 "consider using a `let` binding to increase its lifetime");
1046 err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
1047 let descr = match opt_loan_path(&err.cmt) {
1049 format!("`{}`", self.loan_path_to_string(&*lp))
1051 None => self.cmt_to_string(&*err.cmt),
1053 self.tcx.note_and_explain_region(
1054 &format!("{} would have to be valid for ",
1058 self.tcx.note_and_explain_region(
1059 &format!("...but {} is only valid for ", descr),
1066 pub fn append_loan_path_to_string(&self,
1067 loan_path: &LoanPath<'tcx>,
1069 match loan_path.kind {
1070 LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
1072 out.push_str(&self.tcx.local_var_name_str(id));
1075 LpDowncast(ref lp_base, variant_def_id) => {
1077 self.append_loan_path_to_string(&**lp_base, out);
1078 out.push_str(DOWNCAST_PRINTED_OPERATOR);
1079 out.push_str(&self.tcx.item_path_str(variant_def_id));
1084 LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => {
1085 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1087 mc::NamedField(fname) => {
1089 out.push_str(&fname.as_str());
1091 mc::PositionalField(idx) => {
1093 out.push_str(&idx.to_string());
1098 LpExtend(ref lp_base, _, LpInterior(_, InteriorElement(..))) => {
1099 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1100 out.push_str("[..]");
1103 LpExtend(ref lp_base, _, LpDeref(_)) => {
1105 self.append_loan_path_to_string(&**lp_base, out);
1110 pub fn append_autoderefd_loan_path_to_string(&self,
1111 loan_path: &LoanPath<'tcx>,
1113 match loan_path.kind {
1114 LpExtend(ref lp_base, _, LpDeref(_)) => {
1115 // For a path like `(*x).f` or `(*x)[3]`, autoderef
1116 // rules would normally allow users to omit the `*x`.
1117 // So just serialize such paths to `x.f` or x[3]` respectively.
1118 self.append_autoderefd_loan_path_to_string(&**lp_base, out)
1121 LpDowncast(ref lp_base, variant_def_id) => {
1123 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1125 out.push_str(&self.tcx.item_path_str(variant_def_id));
1129 LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
1130 self.append_loan_path_to_string(loan_path, out)
1135 pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
1136 let mut result = String::new();
1137 self.append_loan_path_to_string(loan_path, &mut result);
1141 pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
1142 cmt.descriptive_string(self.tcx)
1145 pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String {
1146 match opt_loan_path(cmt) {
1147 Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)),
1148 None => self.cmt_to_string(cmt),
1153 fn statement_scope_span(tcx: &ty::ctxt, region: ty::Region) -> Option<Span> {
1155 ty::ReScope(scope) => {
1156 match tcx.map.find(scope.node_id(&tcx.region_maps)) {
1157 Some(hir_map::NodeStmt(stmt)) => Some(stmt.span),
1165 impl BitwiseOperator for LoanDataFlowOperator {
1167 fn join(&self, succ: usize, pred: usize) -> usize {
1168 succ | pred // loans from both preds are in scope
1172 impl DataFlowOperator for LoanDataFlowOperator {
1174 fn initial_value(&self) -> bool {
1175 false // no loans in scope by default
1179 impl<'tcx> fmt::Debug for InteriorKind {
1180 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1182 InteriorField(mc::NamedField(fld)) => write!(f, "{}", fld),
1183 InteriorField(mc::PositionalField(i)) => write!(f, "#{}", i),
1184 InteriorElement(..) => write!(f, "[]"),
1189 impl<'tcx> fmt::Debug for Loan<'tcx> {
1190 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1191 write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})",
1197 self.restricted_paths)
1201 impl<'tcx> fmt::Debug for LoanPath<'tcx> {
1202 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1205 write!(f, "$({})", ty::tls::with(|tcx| tcx.map.node_to_string(id)))
1208 LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1209 let s = ty::tls::with(|tcx| tcx.map.node_to_string(var_id));
1210 write!(f, "$({} captured by id={})", s, closure_expr_id)
1213 LpDowncast(ref lp, variant_def_id) => {
1214 let variant_str = if variant_def_id.is_local() {
1215 ty::tls::with(|tcx| tcx.item_path_str(variant_def_id))
1217 format!("{:?}", variant_def_id)
1219 write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
1222 LpExtend(ref lp, _, LpDeref(_)) => {
1223 write!(f, "{:?}.*", lp)
1226 LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
1227 write!(f, "{:?}.{:?}", lp, interior)
1233 impl<'tcx> fmt::Display for LoanPath<'tcx> {
1234 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1237 write!(f, "$({})", ty::tls::with(|tcx| tcx.map.node_to_user_string(id)))
1240 LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
1241 let s = ty::tls::with(|tcx| tcx.map.node_to_user_string(var_id));
1242 write!(f, "$({} captured by closure)", s)
1245 LpDowncast(ref lp, variant_def_id) => {
1246 let variant_str = if variant_def_id.is_local() {
1247 ty::tls::with(|tcx| tcx.item_path_str(variant_def_id))
1249 format!("{:?}", variant_def_id)
1251 write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
1254 LpExtend(ref lp, _, LpDeref(_)) => {
1255 write!(f, "{}.*", lp)
1258 LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
1259 write!(f, "{}.{:?}", lp, interior)