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::dataflow::KillFrom;
28 use rustc::middle::expr_use_visitor as euv;
29 use rustc::middle::mem_categorization as mc;
30 use rustc::middle::free_region::FreeRegionMap;
31 use rustc::middle::region;
32 use rustc::middle::ty::{self, Ty};
33 use rustc::util::ppaux::{note_and_explain_region, Repr, UserString};
36 use std::string::String;
39 use syntax::ast_map::blocks::{FnLikeNode, FnParts};
41 use syntax::codemap::Span;
42 use syntax::parse::token;
44 use syntax::visit::{Visitor, FnKind};
45 use syntax::ast::{FnDecl, Block, NodeId};
53 #[derive(Clone, Copy)]
54 pub struct LoanDataFlowOperator;
56 pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
58 impl<'a, 'tcx, 'v> Visitor<'v> for BorrowckCtxt<'a, 'tcx> {
59 fn visit_fn(&mut self, fk: FnKind<'v>, fd: &'v FnDecl,
60 b: &'v Block, s: Span, id: ast::NodeId) {
63 visit::FkMethod(..) => {
64 let new_free_region_map = self.tcx.free_region_map(id);
65 let old_free_region_map =
66 mem::replace(&mut self.free_region_map, new_free_region_map);
67 borrowck_fn(self, fk, fd, b, s, id);
68 self.free_region_map = old_free_region_map;
72 borrowck_fn(self, fk, fd, b, s, id);
77 fn visit_item(&mut self, item: &ast::Item) {
78 borrowck_item(self, item);
81 fn visit_trait_item(&mut self, ti: &ast::TraitItem) {
82 if let ast::ConstTraitItem(_, Some(ref expr)) = ti.node {
83 gather_loans::gather_loans_in_static_initializer(self, &*expr);
85 visit::walk_trait_item(self, ti);
88 fn visit_impl_item(&mut self, ii: &ast::ImplItem) {
89 if let ast::ConstImplItem(_, ref expr) = ii.node {
90 gather_loans::gather_loans_in_static_initializer(self, &*expr);
92 visit::walk_impl_item(self, ii);
96 pub fn check_crate(tcx: &ty::ctxt) {
97 let mut bccx = BorrowckCtxt {
99 free_region_map: FreeRegionMap::new(),
101 loaned_paths_same: 0,
108 visit::walk_crate(&mut bccx, tcx.map.krate());
110 if tcx.sess.borrowck_stats() {
111 println!("--- borrowck stats ---");
112 println!("paths requiring guarantees: {}",
113 bccx.stats.guaranteed_paths);
114 println!("paths requiring loans : {}",
115 make_stat(&bccx, bccx.stats.loaned_paths_same));
116 println!("paths requiring imm loans : {}",
117 make_stat(&bccx, bccx.stats.loaned_paths_imm));
118 println!("stable paths : {}",
119 make_stat(&bccx, bccx.stats.stable_paths));
122 fn make_stat(bccx: &BorrowckCtxt, stat: usize) -> String {
123 let total = bccx.stats.guaranteed_paths as f64;
124 let perc = if total == 0.0 { 0.0 } else { stat as f64 * 100.0 / total };
125 format!("{} ({:.0}%)", stat, perc)
129 fn borrowck_item(this: &mut BorrowckCtxt, item: &ast::Item) {
130 // Gather loans for items. Note that we don't need
131 // to check loans for single expressions. The check
132 // loan step is intended for things that have a data
133 // flow dependent conditions.
135 ast::ItemStatic(_, _, ref ex) |
136 ast::ItemConst(_, ref ex) => {
137 gather_loans::gather_loans_in_static_initializer(this, &**ex);
142 visit::walk_item(this, item);
145 /// Collection of conclusions determined via borrow checker analyses.
146 pub struct AnalysisData<'a, 'tcx: 'a> {
147 pub all_loans: Vec<Loan<'tcx>>,
148 pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
149 pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
152 fn borrowck_fn(this: &mut BorrowckCtxt,
158 debug!("borrowck_fn(id={})", id);
159 let cfg = cfg::CFG::new(this.tcx, body);
160 let AnalysisData { all_loans,
162 move_data: flowed_moves } =
163 build_borrowck_dataflow_data(this, fk, decl, &cfg, body, sp, id);
165 move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
170 check_loans::check_loans(this,
178 visit::walk_fn(this, fk, decl, body, sp);
181 fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
188 -> AnalysisData<'a, 'tcx>
190 // Check the body of fn items.
191 let id_range = ast_util::compute_id_range_for_fn_body(fk, decl, body, sp, id);
192 let (all_loans, move_data) =
193 gather_loans::gather_loans_in_fn(this, id, decl, body);
196 DataFlowContext::new(this.tcx,
200 LoanDataFlowOperator,
203 for (loan_idx, loan) in all_loans.iter().enumerate() {
204 loan_dfcx.add_gen(loan.gen_scope.node_id(), loan_idx);
205 loan_dfcx.add_kill(KillFrom::ScopeEnd, loan.kill_scope.node_id(), loan_idx);
207 loan_dfcx.add_kills_from_flow_exits(cfg);
208 loan_dfcx.propagate(cfg, body);
210 let flowed_moves = move_data::FlowedMoveData::new(move_data,
217 AnalysisData { all_loans: all_loans,
219 move_data:flowed_moves }
222 /// This and a `ty::ctxt` is all you need to run the dataflow analyses
223 /// used in the borrow checker.
224 pub struct FnPartsWithCFG<'a> {
225 pub fn_parts: FnParts<'a>,
226 pub cfg: &'a cfg::CFG,
229 impl<'a> FnPartsWithCFG<'a> {
230 pub fn from_fn_like(f: &'a FnLikeNode,
231 g: &'a cfg::CFG) -> FnPartsWithCFG<'a> {
232 FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
236 /// Accessor for introspective clients inspecting `AnalysisData` and
237 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
238 pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
239 tcx: &'a ty::ctxt<'tcx>,
240 input: FnPartsWithCFG<'a>)
241 -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>)
244 let mut bccx = BorrowckCtxt {
246 free_region_map: FreeRegionMap::new(),
248 loaned_paths_same: 0,
255 let p = input.fn_parts;
257 let dataflow_data = build_borrowck_dataflow_data(&mut bccx,
265 (bccx, dataflow_data)
268 // ----------------------------------------------------------------------
271 pub struct BorrowckCtxt<'a, 'tcx: 'a> {
272 tcx: &'a ty::ctxt<'tcx>,
274 // Hacky. As we visit various fns, we have to load up the
275 // free-region map for each one. This map is computed by during
276 // typeck for each fn item and stored -- closures just use the map
277 // from the fn item that encloses them. Since we walk the fns in
278 // order, we basically just overwrite this field as we enter a fn
279 // item and restore it afterwards in a stack-like fashion. Then
280 // the borrow checking code can assume that `free_region_map` is
281 // always the correct map for the current fn. Feels like it'd be
282 // better to just recompute this, rather than store it, but it's a
283 // bit of a pain to factor that code out at the moment.
284 free_region_map: FreeRegionMap,
291 loaned_paths_same: usize,
292 loaned_paths_imm: usize,
294 guaranteed_paths: usize
297 pub type BckResult<'tcx, T> = Result<T, BckError<'tcx>>;
299 ///////////////////////////////////////////////////////////////////////////
300 // Loans and loan paths
302 /// Record of a loan that was issued.
303 pub struct Loan<'tcx> {
305 loan_path: Rc<LoanPath<'tcx>>,
306 kind: ty::BorrowKind,
307 restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
309 /// gen_scope indicates where loan is introduced. Typically the
310 /// loan is introduced at the point of the borrow, but in some
311 /// cases, notably method arguments, the loan may be introduced
312 /// only later, once it comes into scope. See also
313 /// `GatherLoanCtxt::compute_gen_scope`.
314 gen_scope: region::CodeExtent,
316 /// kill_scope indicates when the loan goes out of scope. This is
317 /// either when the lifetime expires or when the local variable
318 /// which roots the loan-path goes out of scope, whichever happens
319 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
320 kill_scope: region::CodeExtent,
322 cause: euv::LoanCause,
325 impl<'tcx> Loan<'tcx> {
326 pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
327 self.loan_path.clone()
331 #[derive(Eq, Hash, Debug)]
332 pub struct LoanPath<'tcx> {
333 kind: LoanPathKind<'tcx>,
337 impl<'tcx> PartialEq for LoanPath<'tcx> {
338 fn eq(&self, that: &LoanPath<'tcx>) -> bool {
339 let r = self.kind == that.kind;
340 debug_assert!(self.ty == that.ty || !r,
341 "Somehow loan paths are equal though their tys are not.");
346 #[derive(PartialEq, Eq, Hash, Debug)]
347 pub enum LoanPathKind<'tcx> {
348 LpVar(ast::NodeId), // `x` in README.md
349 LpUpvar(ty::UpvarId), // `x` captured by-value into closure
350 LpDowncast(Rc<LoanPath<'tcx>>, ast::DefId), // `x` downcast to particular enum variant
351 LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem)
354 impl<'tcx> LoanPath<'tcx> {
355 fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
356 LoanPath { kind: kind, ty: ty }
359 fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
362 // FIXME (pnkfelix): See discussion here
363 // https://github.com/pnkfelix/rust/commit/
364 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
365 const DOWNCAST_PRINTED_OPERATOR: &'static str = " as ";
367 // A local, "cleaned" version of `mc::InteriorKind` that drops
368 // information that is not relevant to loan-path analysis. (In
369 // particular, the distinction between how precisely a array-element
370 // is tracked is irrelevant here.)
371 #[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
372 pub enum InteriorKind {
373 InteriorField(mc::FieldName),
374 InteriorElement(mc::ElementKind),
377 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
378 impl ToInteriorKind for mc::InteriorKind {
379 fn cleaned(self) -> InteriorKind {
381 mc::InteriorField(name) => InteriorField(name),
382 mc::InteriorElement(_, elem_kind) => InteriorElement(elem_kind),
387 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
388 pub enum LoanPathElem {
389 LpDeref(mc::PointerKind), // `*LV` in README.md
390 LpInterior(InteriorKind), // `LV.f` in README.md
393 pub fn closure_to_block(closure_id: ast::NodeId,
394 tcx: &ty::ctxt) -> ast::NodeId {
395 match tcx.map.get(closure_id) {
396 ast_map::NodeExpr(expr) => match expr.node {
397 ast::ExprClosure(_, _, ref block) => {
401 panic!("encountered non-closure id: {}", closure_id)
404 _ => panic!("encountered non-expr id: {}", closure_id)
408 impl<'tcx> LoanPath<'tcx> {
409 pub fn kill_scope(&self, tcx: &ty::ctxt<'tcx>) -> region::CodeExtent {
411 LpVar(local_id) => tcx.region_maps.var_scope(local_id),
412 LpUpvar(upvar_id) => {
413 let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
414 region::CodeExtent::from_node_id(block_id)
416 LpDowncast(ref base, _) |
417 LpExtend(ref base, _, _) => base.kill_scope(tcx),
421 fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
422 match (&self.kind, &other.kind) {
423 (&LpExtend(ref base, _, LpInterior(id)), &LpExtend(ref base2, _, LpInterior(id2))) =>
425 base.has_fork(&**base2)
429 (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
430 (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&**base),
435 fn depth(&self) -> usize {
437 LpExtend(ref base, _, LpDeref(_)) => base.depth(),
438 LpExtend(ref base, _, LpInterior(_)) => base.depth() + 1,
443 fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
444 match (&self.kind, &other.kind) {
445 (&LpExtend(ref base, a, LpInterior(id)),
446 &LpExtend(ref base2, _, LpInterior(id2))) => {
448 base.common(&**base2).map(|x| {
450 if base.depth() == xd && base2.depth() == xd {
451 assert_eq!(base.ty, base2.ty);
452 assert_eq!(self.ty, other.ty);
454 kind: LpExtend(Rc::new(x), a, LpInterior(id)),
462 base.common(&**base2)
465 (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
466 (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&**other),
467 (&LpVar(id), &LpVar(id2)) => {
469 assert_eq!(self.ty, other.ty);
470 Some(LoanPath { kind: LpVar(id), ty: self.ty })
475 (&LpUpvar(id), &LpUpvar(id2)) => {
477 assert_eq!(self.ty, other.ty);
478 Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
488 pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
489 //! Computes the `LoanPath` (if any) for a `cmt`.
490 //! Note that this logic is somewhat duplicated in
491 //! the method `compute()` found in `gather_loans::restrictions`,
492 //! which allows it to share common loan path pieces as it
493 //! traverses the CMT.
495 let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
499 mc::cat_static_item => {
503 mc::cat_local(id) => {
504 Some(new_lp(LpVar(id)))
507 mc::cat_upvar(mc::Upvar { id, .. }) => {
508 Some(new_lp(LpUpvar(id)))
511 mc::cat_deref(ref cmt_base, _, pk) => {
512 opt_loan_path(cmt_base).map(|lp| {
513 new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
517 mc::cat_interior(ref cmt_base, ik) => {
518 opt_loan_path(cmt_base).map(|lp| {
519 new_lp(LpExtend(lp, cmt.mutbl, LpInterior(ik.cleaned())))
523 mc::cat_downcast(ref cmt_base, variant_def_id) =>
524 opt_loan_path(cmt_base)
526 new_lp(LpDowncast(lp, variant_def_id))
532 ///////////////////////////////////////////////////////////////////////////
535 // Errors that can occur
537 pub enum bckerr_code {
539 err_out_of_scope(ty::Region, ty::Region), // superscope, subscope
540 err_borrowed_pointer_too_short(ty::Region, ty::Region), // loan, ptr
543 // Combination of an error code and the categorization of the expression
546 pub struct BckError<'tcx> {
548 cause: euv::LoanCause,
553 #[derive(Copy, Clone)]
554 pub enum AliasableViolationKind {
556 BorrowViolation(euv::LoanCause)
559 #[derive(Copy, Clone, Debug)]
560 pub enum MovedValueUseKind {
565 ///////////////////////////////////////////////////////////////////////////
568 impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
569 pub fn is_subregion_of(&self, r_sub: ty::Region, r_sup: ty::Region)
572 self.free_region_map.is_subregion_of(self.tcx, r_sub, r_sup)
575 pub fn report(&self, err: BckError<'tcx>) {
576 // Catch and handle some particular cases.
577 match (&err.code, &err.cause) {
578 (&err_out_of_scope(ty::ReScope(_), ty::ReStatic), &euv::ClosureCapture(span)) |
579 (&err_out_of_scope(ty::ReScope(_), ty::ReFree(..)), &euv::ClosureCapture(span)) => {
580 return self.report_out_of_scope_escaping_closure_capture(&err, span);
588 &self.bckerr_to_string(&err));
589 self.note_and_explain_bckerr(err);
592 pub fn report_use_of_moved_value<'b>(&self,
594 use_kind: MovedValueUseKind,
596 the_move: &move_data::Move,
597 moved_lp: &LoanPath<'tcx>,
598 param_env: &ty::ParameterEnvironment<'b,'tcx>) {
599 let verb = match use_kind {
601 MovedInCapture => "capture",
604 let (ol, moved_lp_msg) = match the_move.kind {
605 move_data::Declared => {
606 self.tcx.sess.span_err(
608 &format!("{} of possibly uninitialized variable: `{}`",
610 self.loan_path_to_string(lp)));
611 (self.loan_path_to_string(moved_lp),
615 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
616 // normally generate a rather confusing message:
618 // error: use of moved value: `x.b`
619 // note: `x.a` moved here...
621 // What we want to do instead is get the 'common ancestor' of the two moves and
622 // use that for most of the message instead, giving is something like this:
624 // error: use of moved value: `x`
625 // note: `x` moved here (through moving `x.a`)...
627 let common = moved_lp.common(lp);
628 let has_common = common.is_some();
629 let has_fork = moved_lp.has_fork(lp);
630 let (nl, ol, moved_lp_msg) =
631 if has_fork && has_common {
632 let nl = self.loan_path_to_string(&common.unwrap());
634 let moved_lp_msg = format!(" (through moving `{}`)",
635 self.loan_path_to_string(moved_lp));
636 (nl, ol, moved_lp_msg)
638 (self.loan_path_to_string(lp),
639 self.loan_path_to_string(moved_lp),
643 let partial = moved_lp.depth() > lp.depth();
644 let msg = if !has_fork && partial { "partially " }
645 else if has_fork && !has_common { "collaterally "}
647 self.tcx.sess.span_err(
649 &format!("{} of {}moved value: `{}`",
657 match the_move.kind {
658 move_data::Declared => {}
660 move_data::MoveExpr => {
661 let (expr_ty, expr_span) = match self.tcx
664 Some(ast_map::NodeExpr(expr)) => {
665 (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
668 self.tcx.sess.bug(&format!("MoveExpr({}) maps to \
674 let (suggestion, _) =
675 move_suggestion(param_env, expr_span, expr_ty, ("moved by default", ""));
676 // If the two spans are the same, it's because the expression will be evaluated
677 // multiple times. Avoid printing the same span and adjust the wording so it makes
678 // more sense that it's from multiple evalutations.
679 if expr_span == use_span {
681 &format!("`{}` was previously moved here{} because it has type `{}`, \
685 expr_ty.user_string(self.tcx),
688 self.tcx.sess.span_note(
690 &format!("`{}` moved here{} because it has type `{}`, which is {}",
693 expr_ty.user_string(self.tcx),
698 move_data::MovePat => {
699 let pat_ty = ty::node_id_to_type(self.tcx, the_move.id);
700 let span = self.tcx.map.span(the_move.id);
701 self.tcx.sess.span_note(span,
702 &format!("`{}` moved here{} because it has type `{}`, \
703 which is moved by default",
706 pat_ty.user_string(self.tcx)));
707 self.tcx.sess.fileline_help(span,
708 "use `ref` to override");
711 move_data::Captured => {
712 let (expr_ty, expr_span) = match self.tcx
715 Some(ast_map::NodeExpr(expr)) => {
716 (ty::expr_ty_adjusted(self.tcx, &*expr), expr.span)
719 self.tcx.sess.bug(&format!("Captured({}) maps to \
725 let (suggestion, help) =
726 move_suggestion(param_env,
730 "make a copy and capture that instead to override"));
731 self.tcx.sess.span_note(
733 &format!("`{}` moved into closure environment here{} because it \
734 has type `{}`, which is {}",
737 expr_ty.user_string(self.tcx),
739 self.tcx.sess.fileline_help(expr_span, help);
743 fn move_suggestion<'a,'tcx>(param_env: &ty::ParameterEnvironment<'a,'tcx>,
746 default_msgs: (&'static str, &'static str))
747 -> (&'static str, &'static str) {
750 if ty::type_moves_by_default(param_env, span, ty) {
752 "perhaps you meant to use `clone()`?")
761 pub fn report_partial_reinitialization_of_uninitialized_structure(
764 lp: &LoanPath<'tcx>) {
768 &format!("partial reinitialization of uninitialized \
770 self.loan_path_to_string(lp)));
773 pub fn report_reassigned_immutable_variable(&self,
777 &move_data::Assignment) {
778 self.tcx.sess.span_err(
780 &format!("re-assignment of immutable variable `{}`",
781 self.loan_path_to_string(lp)));
782 self.tcx.sess.span_note(assign.span, "prior assignment occurs here");
785 pub fn span_err(&self, s: Span, m: &str) {
786 self.tcx.sess.span_err(s, m);
789 pub fn span_bug(&self, s: Span, m: &str) {
790 self.tcx.sess.span_bug(s, m);
793 pub fn span_note(&self, s: Span, m: &str) {
794 self.tcx.sess.span_note(s, m);
797 pub fn span_end_note(&self, s: Span, m: &str) {
798 self.tcx.sess.span_end_note(s, m);
801 pub fn span_help(&self, s: Span, m: &str) {
802 self.tcx.sess.span_help(s, m);
805 pub fn fileline_help(&self, s: Span, m: &str) {
806 self.tcx.sess.fileline_help(s, m);
809 pub fn bckerr_to_string(&self, err: &BckError<'tcx>) -> String {
812 let descr = match err.cmt.note {
813 mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
814 self.cmt_to_string(&*err.cmt)
816 _ => match opt_loan_path(&err.cmt) {
819 err.cmt.mutbl.to_user_str(),
820 self.cmt_to_string(&*err.cmt))
823 format!("{} {} `{}`",
824 err.cmt.mutbl.to_user_str(),
825 self.cmt_to_string(&*err.cmt),
826 self.loan_path_to_string(&*lp))
832 euv::ClosureCapture(_) => {
833 format!("closure cannot assign to {}", descr)
835 euv::OverloadedOperator |
841 euv::MatchDiscriminant => {
842 format!("cannot borrow {} as mutable", descr)
844 euv::ClosureInvocation => {
845 self.tcx.sess.span_bug(err.span,
846 "err_mutbl with a closure invocation");
850 err_out_of_scope(..) => {
851 let msg = match opt_loan_path(&err.cmt) {
852 None => "borrowed value".to_string(),
854 format!("`{}`", self.loan_path_to_string(&*lp))
857 format!("{} does not live long enough", msg)
859 err_borrowed_pointer_too_short(..) => {
860 let descr = self.cmt_to_path_or_string(&err.cmt);
861 format!("lifetime of {} is too short to guarantee \
862 its contents can be safely reborrowed",
868 pub fn report_aliasability_violation(&self,
870 kind: AliasableViolationKind,
871 cause: mc::AliasableReason) {
872 let mut is_closure = false;
873 let prefix = match kind {
874 MutabilityViolation => {
875 "cannot assign to data"
877 BorrowViolation(euv::ClosureCapture(_)) |
878 BorrowViolation(euv::OverloadedOperator) |
879 BorrowViolation(euv::AddrOf) |
880 BorrowViolation(euv::AutoRef) |
881 BorrowViolation(euv::AutoUnsafe) |
882 BorrowViolation(euv::RefBinding) |
883 BorrowViolation(euv::MatchDiscriminant) => {
884 "cannot borrow data mutably"
887 BorrowViolation(euv::ClosureInvocation) => {
892 BorrowViolation(euv::ForLoop) => {
898 mc::AliasableOther => {
899 self.tcx.sess.span_err(
901 &format!("{} in an aliasable location",
904 mc::AliasableReason::UnaliasableImmutable => {
905 self.tcx.sess.span_err(
907 &format!("{} in an immutable container",
910 mc::AliasableClosure(id) => {
911 self.tcx.sess.span_err(span,
912 &format!("{} in a captured outer \
913 variable in an `Fn` closure", prefix));
914 if let BorrowViolation(euv::ClosureCapture(_)) = kind {
915 // The aliasability violation with closure captures can
916 // happen for nested closures, so we know the enclosing
917 // closure incorrectly accepts an `Fn` while it needs to
919 span_help!(self.tcx.sess, self.tcx.map.span(id),
920 "consider changing this to accept closures that implement `FnMut`");
922 span_help!(self.tcx.sess, self.tcx.map.span(id),
923 "consider changing this closure to take self by mutable reference");
926 mc::AliasableStatic(..) |
927 mc::AliasableStaticMut(..) => {
928 self.tcx.sess.span_err(
930 &format!("{} in a static location", prefix));
932 mc::AliasableBorrowed => {
933 self.tcx.sess.span_err(
935 &format!("{} in a `&` reference", prefix));
940 self.tcx.sess.fileline_help(
942 "closures behind references must be called via `&mut`");
946 fn report_out_of_scope_escaping_closure_capture(&self,
947 err: &BckError<'tcx>,
950 let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt);
953 self.tcx.sess, err.span, E0373,
954 "closure may outlive the current function, \
956 which is owned by the current function",
959 self.tcx.sess.span_note(
961 &format!("{} is borrowed here",
962 cmt_path_or_string));
965 match self.tcx.sess.codemap().span_to_snippet(err.span) {
966 Ok(string) => format!("move {}", string),
967 Err(_) => format!("move |<args>| <body>")
970 self.tcx.sess.span_suggestion(
972 &format!("to force the closure to take ownership of {} \
973 (and any other referenced variables), \
974 use the `move` keyword, as shown:",
979 pub fn note_and_explain_bckerr(&self, err: BckError<'tcx>) {
984 mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
985 // If this is an `Fn` closure, it simply can't mutate upvars.
986 // If it's an `FnMut` closure, the original variable was declared immutable.
987 // We need to determine which is the case here.
988 let kind = match err.cmt.upvar().unwrap().cat {
989 mc::cat_upvar(mc::Upvar { kind, .. }) => kind,
992 if kind == ty::FnClosureKind {
993 self.tcx.sess.span_help(
994 self.tcx.map.span(upvar_id.closure_expr_id),
995 "consider changing this closure to take \
996 self by mutable reference");
1003 err_out_of_scope(super_scope, sub_scope) => {
1004 note_and_explain_region(
1006 "reference must be valid for ",
1009 let suggestion = if is_statement_scope(self.tcx, super_scope) {
1010 Some("consider using a `let` binding to increase its lifetime")
1014 let span = note_and_explain_region(
1016 "...but borrowed value is only valid for ",
1019 match (span, suggestion) {
1021 (Some(span), Some(msg)) => self.tcx.sess.span_help(span, msg),
1022 (None, Some(msg)) => self.tcx.sess.help(msg),
1026 err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
1027 let descr = match opt_loan_path(&err.cmt) {
1029 format!("`{}`", self.loan_path_to_string(&*lp))
1031 None => self.cmt_to_string(&*err.cmt),
1033 note_and_explain_region(
1035 &format!("{} would have to be valid for ",
1039 note_and_explain_region(
1041 &format!("...but {} is only valid for ", descr),
1048 pub fn append_loan_path_to_string(&self,
1049 loan_path: &LoanPath<'tcx>,
1051 match loan_path.kind {
1052 LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
1054 out.push_str(&ty::local_var_name_str(self.tcx, id));
1057 LpDowncast(ref lp_base, variant_def_id) => {
1059 self.append_loan_path_to_string(&**lp_base, out);
1060 out.push_str(DOWNCAST_PRINTED_OPERATOR);
1061 out.push_str(&ty::item_path_str(self.tcx, variant_def_id));
1066 LpExtend(ref lp_base, _, LpInterior(InteriorField(fname))) => {
1067 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1069 mc::NamedField(fname) => {
1071 out.push_str(&token::get_name(fname));
1073 mc::PositionalField(idx) => {
1075 out.push_str(&idx.to_string());
1080 LpExtend(ref lp_base, _, LpInterior(InteriorElement(..))) => {
1081 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1082 out.push_str("[..]");
1085 LpExtend(ref lp_base, _, LpDeref(_)) => {
1087 self.append_loan_path_to_string(&**lp_base, out);
1092 pub fn append_autoderefd_loan_path_to_string(&self,
1093 loan_path: &LoanPath<'tcx>,
1095 match loan_path.kind {
1096 LpExtend(ref lp_base, _, LpDeref(_)) => {
1097 // For a path like `(*x).f` or `(*x)[3]`, autoderef
1098 // rules would normally allow users to omit the `*x`.
1099 // So just serialize such paths to `x.f` or x[3]` respectively.
1100 self.append_autoderefd_loan_path_to_string(&**lp_base, out)
1103 LpDowncast(ref lp_base, variant_def_id) => {
1105 self.append_autoderefd_loan_path_to_string(&**lp_base, out);
1107 out.push_str(&ty::item_path_str(self.tcx, variant_def_id));
1111 LpVar(..) | LpUpvar(..) | LpExtend(_, _, LpInterior(..)) => {
1112 self.append_loan_path_to_string(loan_path, out)
1117 pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
1118 let mut result = String::new();
1119 self.append_loan_path_to_string(loan_path, &mut result);
1123 pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
1124 cmt.descriptive_string(self.tcx)
1127 pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String {
1128 match opt_loan_path(cmt) {
1129 Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)),
1130 None => self.cmt_to_string(cmt),
1135 fn is_statement_scope(tcx: &ty::ctxt, region: ty::Region) -> bool {
1137 ty::ReScope(scope) => {
1138 match tcx.map.find(scope.node_id()) {
1139 Some(ast_map::NodeStmt(_)) => true,
1147 impl BitwiseOperator for LoanDataFlowOperator {
1149 fn join(&self, succ: usize, pred: usize) -> usize {
1150 succ | pred // loans from both preds are in scope
1154 impl DataFlowOperator for LoanDataFlowOperator {
1156 fn initial_value(&self) -> bool {
1157 false // no loans in scope by default
1161 impl<'tcx> Repr<'tcx> for InteriorKind {
1162 fn repr(&self, _tcx: &ty::ctxt<'tcx>) -> String {
1164 InteriorField(mc::NamedField(fld)) =>
1165 format!("{}", token::get_name(fld)),
1166 InteriorField(mc::PositionalField(i)) => format!("#{}", i),
1167 InteriorElement(..) => "[]".to_string(),
1172 impl<'tcx> Repr<'tcx> for Loan<'tcx> {
1173 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1174 format!("Loan_{}({}, {:?}, {:?}-{:?}, {})",
1176 self.loan_path.repr(tcx),
1180 self.restricted_paths.repr(tcx))
1184 impl<'tcx> Repr<'tcx> for LoanPath<'tcx> {
1185 fn repr(&self, tcx: &ty::ctxt<'tcx>) -> String {
1188 format!("$({})", tcx.map.node_to_string(id))
1191 LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1192 let s = tcx.map.node_to_string(var_id);
1193 format!("$({} captured by id={})", s, closure_expr_id)
1196 LpDowncast(ref lp, variant_def_id) => {
1197 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1198 ty::item_path_str(tcx, variant_def_id)
1200 variant_def_id.repr(tcx)
1202 format!("({}{}{})", lp.repr(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1205 LpExtend(ref lp, _, LpDeref(_)) => {
1206 format!("{}.*", lp.repr(tcx))
1209 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1210 format!("{}.{}", lp.repr(tcx), interior.repr(tcx))
1216 impl<'tcx> UserString<'tcx> for LoanPath<'tcx> {
1217 fn user_string(&self, tcx: &ty::ctxt<'tcx>) -> String {
1220 format!("$({})", tcx.map.node_to_user_string(id))
1223 LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
1224 let s = tcx.map.node_to_user_string(var_id);
1225 format!("$({} captured by closure)", s)
1228 LpDowncast(ref lp, variant_def_id) => {
1229 let variant_str = if variant_def_id.krate == ast::LOCAL_CRATE {
1230 ty::item_path_str(tcx, variant_def_id)
1232 variant_def_id.repr(tcx)
1234 format!("({}{}{})", lp.user_string(tcx), DOWNCAST_PRINTED_OPERATOR, variant_str)
1237 LpExtend(ref lp, _, LpDeref(_)) => {
1238 format!("{}.*", lp.user_string(tcx))
1241 LpExtend(ref lp, _, LpInterior(ref interior)) => {
1242 format!("{}.{}", lp.user_string(tcx), interior.repr(tcx))