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 pub use self::mir::elaborate_drops::ElaborateDrops;
23 use self::InteriorKind::*;
25 use rustc::dep_graph::DepNode;
26 use rustc::hir::map as hir_map;
27 use rustc::hir::map::blocks::FnLikeNode;
29 use rustc::middle::dataflow::DataFlowContext;
30 use rustc::middle::dataflow::BitwiseOperator;
31 use rustc::middle::dataflow::DataFlowOperator;
32 use rustc::middle::dataflow::KillFrom;
33 use rustc::hir::def_id::DefId;
34 use rustc::middle::expr_use_visitor as euv;
35 use rustc::middle::mem_categorization as mc;
36 use rustc::middle::mem_categorization::Categorization;
37 use rustc::middle::region;
38 use rustc::ty::{self, TyCtxt};
42 use std::hash::{Hash, Hasher};
44 use syntax_pos::{MultiSpan, Span};
45 use errors::DiagnosticBuilder;
48 use rustc::hir::intravisit::{self, Visitor};
58 #[derive(Clone, Copy)]
59 pub struct LoanDataFlowOperator;
61 pub type LoanDataFlow<'a, 'tcx> = DataFlowContext<'a, 'tcx, LoanDataFlowOperator>;
63 pub fn check_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) {
64 tcx.dep_graph.with_task(DepNode::BorrowCheckKrate, tcx, (), check_crate_task);
66 fn check_crate_task<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, (): ()) {
67 tcx.visit_all_bodies_in_krate(|body_owner_def_id, body_id| {
68 tcx.dep_graph.with_task(DepNode::BorrowCheck(body_owner_def_id),
76 /// Collection of conclusions determined via borrow checker analyses.
77 pub struct AnalysisData<'a, 'tcx: 'a> {
78 pub all_loans: Vec<Loan<'tcx>>,
79 pub loans: DataFlowContext<'a, 'tcx, LoanDataFlowOperator>,
80 pub move_data: move_data::FlowedMoveData<'a, 'tcx>,
83 fn borrowck_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, body_id: hir::BodyId) {
84 debug!("borrowck_fn(body_id={:?})", body_id);
86 let owner_id = tcx.hir.body_owner(body_id);
87 let owner_def_id = tcx.hir.local_def_id(owner_id);
88 let attributes = tcx.get_attrs(owner_def_id);
89 let tables = tcx.item_tables(owner_def_id);
91 let mut bccx = &mut BorrowckCtxt {
96 let body = bccx.tcx.hir.body(body_id);
98 if bccx.tcx.has_attr(owner_def_id, "rustc_mir_borrowck") {
99 mir::borrowck_mir(bccx, owner_id, &attributes);
102 let cfg = cfg::CFG::new(bccx.tcx, &body);
103 let AnalysisData { all_loans,
105 move_data: flowed_moves } =
106 build_borrowck_dataflow_data(bccx, &cfg, body_id);
108 move_data::fragments::instrument_move_fragments(&flowed_moves.move_data,
111 move_data::fragments::build_unfragmented_map(bccx,
112 &flowed_moves.move_data,
115 check_loans::check_loans(bccx, &loan_dfcx, &flowed_moves, &all_loans[..], body);
118 fn build_borrowck_dataflow_data<'a, 'tcx>(this: &mut BorrowckCtxt<'a, 'tcx>,
120 body_id: hir::BodyId)
121 -> AnalysisData<'a, 'tcx>
123 // Check the body of fn items.
125 let body = tcx.hir.body(body_id);
127 let mut visitor = intravisit::IdRangeComputingVisitor::new(&tcx.hir);
128 visitor.visit_body(body);
131 let (all_loans, move_data) =
132 gather_loans::gather_loans_in_fn(this, body_id);
135 DataFlowContext::new(this.tcx,
139 LoanDataFlowOperator,
142 for (loan_idx, loan) in all_loans.iter().enumerate() {
143 loan_dfcx.add_gen(loan.gen_scope.node_id(&tcx.region_maps), loan_idx);
144 loan_dfcx.add_kill(KillFrom::ScopeEnd,
145 loan.kill_scope.node_id(&tcx.region_maps), loan_idx);
147 loan_dfcx.add_kills_from_flow_exits(cfg);
148 loan_dfcx.propagate(cfg, body);
150 let flowed_moves = move_data::FlowedMoveData::new(move_data,
156 AnalysisData { all_loans: all_loans,
158 move_data:flowed_moves }
161 /// Accessor for introspective clients inspecting `AnalysisData` and
162 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
163 pub fn build_borrowck_dataflow_data_for_fn<'a, 'tcx>(
164 tcx: TyCtxt<'a, 'tcx, 'tcx>,
165 body_id: hir::BodyId,
167 -> (BorrowckCtxt<'a, 'tcx>, AnalysisData<'a, 'tcx>)
169 let owner_id = tcx.hir.body_owner(body_id);
170 let owner_def_id = tcx.hir.local_def_id(owner_id);
171 let tables = tcx.item_tables(owner_def_id);
173 let mut bccx = BorrowckCtxt {
178 let dataflow_data = build_borrowck_dataflow_data(&mut bccx, cfg, body_id);
179 (bccx, dataflow_data)
182 // ----------------------------------------------------------------------
185 pub struct BorrowckCtxt<'a, 'tcx: 'a> {
186 tcx: TyCtxt<'a, 'tcx, 'tcx>,
188 // tables for the current thing we are checking; set to
189 // Some in `borrowck_fn` and cleared later
190 tables: &'a ty::TypeckTables<'tcx>,
193 ///////////////////////////////////////////////////////////////////////////
194 // Loans and loan paths
196 /// Record of a loan that was issued.
197 pub struct Loan<'tcx> {
199 loan_path: Rc<LoanPath<'tcx>>,
200 kind: ty::BorrowKind,
201 restricted_paths: Vec<Rc<LoanPath<'tcx>>>,
203 /// gen_scope indicates where loan is introduced. Typically the
204 /// loan is introduced at the point of the borrow, but in some
205 /// cases, notably method arguments, the loan may be introduced
206 /// only later, once it comes into scope. See also
207 /// `GatherLoanCtxt::compute_gen_scope`.
208 gen_scope: region::CodeExtent,
210 /// kill_scope indicates when the loan goes out of scope. This is
211 /// either when the lifetime expires or when the local variable
212 /// which roots the loan-path goes out of scope, whichever happens
213 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
214 kill_scope: region::CodeExtent,
216 cause: euv::LoanCause,
219 impl<'tcx> Loan<'tcx> {
220 pub fn loan_path(&self) -> Rc<LoanPath<'tcx>> {
221 self.loan_path.clone()
226 pub struct LoanPath<'tcx> {
227 kind: LoanPathKind<'tcx>,
231 impl<'tcx> PartialEq for LoanPath<'tcx> {
232 fn eq(&self, that: &LoanPath<'tcx>) -> bool {
233 self.kind == that.kind
237 impl<'tcx> Hash for LoanPath<'tcx> {
238 fn hash<H: Hasher>(&self, state: &mut H) {
239 self.kind.hash(state);
243 #[derive(PartialEq, Eq, Hash, Debug)]
244 pub enum LoanPathKind<'tcx> {
245 LpVar(ast::NodeId), // `x` in README.md
246 LpUpvar(ty::UpvarId), // `x` captured by-value into closure
247 LpDowncast(Rc<LoanPath<'tcx>>, DefId), // `x` downcast to particular enum variant
248 LpExtend(Rc<LoanPath<'tcx>>, mc::MutabilityCategory, LoanPathElem<'tcx>)
251 impl<'tcx> LoanPath<'tcx> {
252 fn new(kind: LoanPathKind<'tcx>, ty: ty::Ty<'tcx>) -> LoanPath<'tcx> {
253 LoanPath { kind: kind, ty: ty }
256 fn to_type(&self) -> ty::Ty<'tcx> { self.ty }
259 // FIXME (pnkfelix): See discussion here
260 // https://github.com/pnkfelix/rust/commit/
261 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
262 const DOWNCAST_PRINTED_OPERATOR: &'static str = " as ";
264 // A local, "cleaned" version of `mc::InteriorKind` that drops
265 // information that is not relevant to loan-path analysis. (In
266 // particular, the distinction between how precisely an array-element
267 // is tracked is irrelevant here.)
268 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
269 pub enum InteriorKind {
270 InteriorField(mc::FieldName),
271 InteriorElement(mc::ElementKind),
274 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
275 impl ToInteriorKind for mc::InteriorKind {
276 fn cleaned(self) -> InteriorKind {
278 mc::InteriorField(name) => InteriorField(name),
279 mc::InteriorElement(_, elem_kind) => InteriorElement(elem_kind),
285 // - a pointer dereference (`*LV` in README.md)
286 // - a field reference, with an optional definition of the containing
287 // enum variant (`LV.f` in README.md)
288 // `DefId` is present when the field is part of struct that is in
289 // a variant of an enum. For instance in:
290 // `enum E { X { foo: u32 }, Y { foo: u32 }}`
291 // each `foo` is qualified by the definitition id of the variant (`X` or `Y`).
292 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
293 pub enum LoanPathElem<'tcx> {
294 LpDeref(mc::PointerKind<'tcx>),
295 LpInterior(Option<DefId>, InteriorKind),
298 pub fn closure_to_block(closure_id: ast::NodeId,
299 tcx: TyCtxt) -> ast::NodeId {
300 match tcx.hir.get(closure_id) {
301 hir_map::NodeExpr(expr) => match expr.node {
302 hir::ExprClosure(.., body_id, _) => {
306 bug!("encountered non-closure id: {}", closure_id)
309 _ => bug!("encountered non-expr id: {}", closure_id)
313 impl<'a, 'tcx> LoanPath<'tcx> {
314 pub fn kill_scope(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> region::CodeExtent {
316 LpVar(local_id) => tcx.region_maps.var_scope(local_id),
317 LpUpvar(upvar_id) => {
318 let block_id = closure_to_block(upvar_id.closure_expr_id, tcx);
319 tcx.region_maps.node_extent(block_id)
321 LpDowncast(ref base, _) |
322 LpExtend(ref base, ..) => base.kill_scope(tcx),
326 fn has_fork(&self, other: &LoanPath<'tcx>) -> bool {
327 match (&self.kind, &other.kind) {
328 (&LpExtend(ref base, _, LpInterior(opt_variant_id, id)),
329 &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) =>
330 if id == id2 && opt_variant_id == opt_variant_id2 {
331 base.has_fork(&base2)
335 (&LpExtend(ref base, _, LpDeref(_)), _) => base.has_fork(other),
336 (_, &LpExtend(ref base, _, LpDeref(_))) => self.has_fork(&base),
341 fn depth(&self) -> usize {
343 LpExtend(ref base, _, LpDeref(_)) => base.depth(),
344 LpExtend(ref base, _, LpInterior(..)) => base.depth() + 1,
349 fn common(&self, other: &LoanPath<'tcx>) -> Option<LoanPath<'tcx>> {
350 match (&self.kind, &other.kind) {
351 (&LpExtend(ref base, a, LpInterior(opt_variant_id, id)),
352 &LpExtend(ref base2, _, LpInterior(opt_variant_id2, id2))) => {
353 if id == id2 && opt_variant_id == opt_variant_id2 {
354 base.common(&base2).map(|x| {
356 if base.depth() == xd && base2.depth() == xd {
358 kind: LpExtend(Rc::new(x), a, LpInterior(opt_variant_id, id)),
369 (&LpExtend(ref base, _, LpDeref(_)), _) => base.common(other),
370 (_, &LpExtend(ref other, _, LpDeref(_))) => self.common(&other),
371 (&LpVar(id), &LpVar(id2)) => {
373 Some(LoanPath { kind: LpVar(id), ty: self.ty })
378 (&LpUpvar(id), &LpUpvar(id2)) => {
380 Some(LoanPath { kind: LpUpvar(id), ty: self.ty })
390 pub fn opt_loan_path<'tcx>(cmt: &mc::cmt<'tcx>) -> Option<Rc<LoanPath<'tcx>>> {
391 //! Computes the `LoanPath` (if any) for a `cmt`.
392 //! Note that this logic is somewhat duplicated in
393 //! the method `compute()` found in `gather_loans::restrictions`,
394 //! which allows it to share common loan path pieces as it
395 //! traverses the CMT.
397 let new_lp = |v: LoanPathKind<'tcx>| Rc::new(LoanPath::new(v, cmt.ty));
400 Categorization::Rvalue(..) |
401 Categorization::StaticItem => {
405 Categorization::Local(id) => {
406 Some(new_lp(LpVar(id)))
409 Categorization::Upvar(mc::Upvar { id, .. }) => {
410 Some(new_lp(LpUpvar(id)))
413 Categorization::Deref(ref cmt_base, _, pk) => {
414 opt_loan_path(cmt_base).map(|lp| {
415 new_lp(LpExtend(lp, cmt.mutbl, LpDeref(pk)))
419 Categorization::Interior(ref cmt_base, ik) => {
420 opt_loan_path(cmt_base).map(|lp| {
421 let opt_variant_id = match cmt_base.cat {
422 Categorization::Downcast(_, did) => Some(did),
425 new_lp(LpExtend(lp, cmt.mutbl, LpInterior(opt_variant_id, ik.cleaned())))
429 Categorization::Downcast(ref cmt_base, variant_def_id) =>
430 opt_loan_path(cmt_base)
432 new_lp(LpDowncast(lp, variant_def_id))
438 ///////////////////////////////////////////////////////////////////////////
441 // Errors that can occur
442 #[derive(Debug, PartialEq)]
443 pub enum bckerr_code<'tcx> {
445 /// superscope, subscope, loan cause
446 err_out_of_scope(&'tcx ty::Region, &'tcx ty::Region, euv::LoanCause),
447 err_borrowed_pointer_too_short(&'tcx ty::Region, &'tcx ty::Region), // loan, ptr
450 // Combination of an error code and the categorization of the expression
452 #[derive(Debug, PartialEq)]
453 pub struct BckError<'tcx> {
455 cause: AliasableViolationKind,
457 code: bckerr_code<'tcx>
460 #[derive(Copy, Clone, Debug, PartialEq)]
461 pub enum AliasableViolationKind {
463 BorrowViolation(euv::LoanCause)
466 #[derive(Copy, Clone, Debug)]
467 pub enum MovedValueUseKind {
472 ///////////////////////////////////////////////////////////////////////////
475 impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> {
476 pub fn is_subregion_of(&self,
477 r_sub: &'tcx ty::Region,
478 r_sup: &'tcx ty::Region)
481 self.tables.free_region_map.is_subregion_of(self.tcx, r_sub, r_sup)
484 pub fn report(&self, err: BckError<'tcx>) {
485 // Catch and handle some particular cases.
486 match (&err.code, &err.cause) {
487 (&err_out_of_scope(&ty::ReScope(_), &ty::ReStatic, _),
488 &BorrowViolation(euv::ClosureCapture(span))) |
489 (&err_out_of_scope(&ty::ReScope(_), &ty::ReFree(..), _),
490 &BorrowViolation(euv::ClosureCapture(span))) => {
491 return self.report_out_of_scope_escaping_closure_capture(&err, span);
496 let mut db = self.bckerr_to_diag(&err);
497 self.note_and_explain_bckerr(&mut db, err);
501 pub fn report_use_of_moved_value(&self,
503 use_kind: MovedValueUseKind,
505 the_move: &move_data::Move,
506 moved_lp: &LoanPath<'tcx>,
507 _param_env: &ty::ParameterEnvironment<'tcx>) {
508 let (verb, verb_participle) = match use_kind {
509 MovedInUse => ("use", "used"),
510 MovedInCapture => ("capture", "captured"),
513 let (_ol, _moved_lp_msg, mut err) = match the_move.kind {
514 move_data::Declared => {
515 // If this is an uninitialized variable, just emit a simple warning
518 self.tcx.sess, use_span, E0381,
519 "{} of possibly uninitialized variable: `{}`",
521 self.loan_path_to_string(lp))
522 .span_label(use_span, &format!("use of possibly uninitialized `{}`",
523 self.loan_path_to_string(lp)))
528 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
529 // normally generate a rather confusing message:
531 // error: use of moved value: `x.b`
532 // note: `x.a` moved here...
534 // What we want to do instead is get the 'common ancestor' of the two moves and
535 // use that for most of the message instead, giving is something like this:
537 // error: use of moved value: `x`
538 // note: `x` moved here (through moving `x.a`)...
540 let common = moved_lp.common(lp);
541 let has_common = common.is_some();
542 let has_fork = moved_lp.has_fork(lp);
543 let (nl, ol, moved_lp_msg) =
544 if has_fork && has_common {
545 let nl = self.loan_path_to_string(&common.unwrap());
547 let moved_lp_msg = format!(" (through moving `{}`)",
548 self.loan_path_to_string(moved_lp));
549 (nl, ol, moved_lp_msg)
551 (self.loan_path_to_string(lp),
552 self.loan_path_to_string(moved_lp),
556 let partial = moved_lp.depth() > lp.depth();
557 let msg = if !has_fork && partial { "partially " }
558 else if has_fork && !has_common { "collaterally "}
560 let err = struct_span_err!(
561 self.tcx.sess, use_span, E0382,
562 "{} of {}moved value: `{}`",
564 (ol, moved_lp_msg, err)}
567 // Get type of value and span where it was previously
569 let (move_span, move_note) = match the_move.kind {
570 move_data::Declared => {
574 move_data::MoveExpr |
575 move_data::MovePat =>
576 (self.tcx.hir.span(the_move.id), ""),
578 move_data::Captured =>
579 (match self.tcx.hir.expect_expr(the_move.id).node {
580 hir::ExprClosure(.., fn_decl_span) => fn_decl_span,
581 ref r => bug!("Captured({}) maps to non-closure: {:?}",
583 }, " (into closure)"),
586 // Annotate the use and the move in the span. Watch out for
587 // the case where the use and the move are the same. This
588 // means the use is in a loop.
589 err = if use_span == move_span {
592 &format!("value moved{} here in previous iteration of loop",
596 err.span_label(use_span, &format!("value {} here after move", verb_participle))
597 .span_label(move_span, &format!("value moved{} here", move_note));
601 err.note(&format!("move occurs because `{}` has type `{}`, \
602 which does not implement the `Copy` trait",
603 self.loan_path_to_string(moved_lp),
606 // Note: we used to suggest adding a `ref binding` or calling
607 // `clone` but those suggestions have been removed because
608 // they are often not what you actually want to do, and were
609 // not considered particularly helpful.
614 pub fn report_partial_reinitialization_of_uninitialized_structure(
617 lp: &LoanPath<'tcx>) {
619 self.tcx.sess, span, E0383,
620 "partial reinitialization of uninitialized structure `{}`",
621 self.loan_path_to_string(lp));
624 pub fn report_reassigned_immutable_variable(&self,
628 &move_data::Assignment) {
629 let mut err = struct_span_err!(
630 self.tcx.sess, span, E0384,
631 "re-assignment of immutable variable `{}`",
632 self.loan_path_to_string(lp));
633 err.span_label(span, &format!("re-assignment of immutable variable"));
634 if span != assign.span {
635 err.span_label(assign.span, &format!("first assignment to `{}`",
636 self.loan_path_to_string(lp)));
641 pub fn span_err(&self, s: Span, m: &str) {
642 self.tcx.sess.span_err(s, m);
645 pub fn struct_span_err<S: Into<MultiSpan>>(&self, s: S, m: &str)
646 -> DiagnosticBuilder<'a> {
647 self.tcx.sess.struct_span_err(s, m)
650 pub fn struct_span_err_with_code<S: Into<MultiSpan>>(&self,
654 -> DiagnosticBuilder<'a> {
655 self.tcx.sess.struct_span_err_with_code(s, msg, code)
658 pub fn span_err_with_code<S: Into<MultiSpan>>(&self, s: S, msg: &str, code: &str) {
659 self.tcx.sess.span_err_with_code(s, msg, code);
662 pub fn bckerr_to_diag(&self, err: &BckError<'tcx>) -> DiagnosticBuilder<'a> {
663 let span = err.span.clone();
664 let mut immutable_field = None;
666 let msg = &match err.code {
668 let descr = match err.cmt.note {
669 mc::NoteClosureEnv(_) | mc::NoteUpvarRef(_) => {
670 self.cmt_to_string(&err.cmt)
672 _ => match opt_loan_path(&err.cmt) {
675 err.cmt.mutbl.to_user_str(),
676 self.cmt_to_string(&err.cmt))
680 format!("{} {} `{}`",
681 err.cmt.mutbl.to_user_str(),
682 self.cmt_to_string(&err.cmt),
683 self.loan_path_to_string(&lp))
689 MutabilityViolation => {
690 format!("cannot assign to {}", descr)
692 BorrowViolation(euv::ClosureCapture(_)) => {
693 format!("closure cannot assign to {}", descr)
695 BorrowViolation(euv::OverloadedOperator) |
696 BorrowViolation(euv::AddrOf) |
697 BorrowViolation(euv::RefBinding) |
698 BorrowViolation(euv::AutoRef) |
699 BorrowViolation(euv::AutoUnsafe) |
700 BorrowViolation(euv::ForLoop) |
701 BorrowViolation(euv::MatchDiscriminant) => {
702 // Check for this field's definition to see if it is an immutable reference
703 // and suggest making it mutable if that is the case.
704 immutable_field = err.cmt.get_field_name()
705 .and_then(|name| err.cmt.get_field(name))
706 .and_then(|did| self.tcx.hir.as_local_node_id(did))
708 if let hir_map::Node::NodeField(ref field) = self.tcx.hir.get(nid) {
709 return self.suggest_mut_for_immutable(&field.ty)
710 .map(|msg| (self.tcx.hir.span(nid), msg));
715 format!("cannot borrow {} as mutable", descr)
717 BorrowViolation(euv::ClosureInvocation) => {
719 "err_mutbl with a closure invocation");
723 err_out_of_scope(..) => {
724 let msg = match opt_loan_path(&err.cmt) {
725 None => "borrowed value".to_string(),
727 format!("`{}`", self.loan_path_to_string(&lp))
730 format!("{} does not live long enough", msg)
732 err_borrowed_pointer_too_short(..) => {
733 let descr = self.cmt_to_path_or_string(&err.cmt);
734 format!("lifetime of {} is too short to guarantee \
735 its contents can be safely reborrowed",
740 let mut db = self.struct_span_err(span, msg);
741 if let Some((span, msg)) = immutable_field {
742 db.span_label(span, &msg);
747 pub fn report_aliasability_violation(&self,
749 kind: AliasableViolationKind,
750 cause: mc::AliasableReason,
751 cmt: mc::cmt<'tcx>) {
752 let mut is_closure = false;
753 let prefix = match kind {
754 MutabilityViolation => {
755 "cannot assign to data"
757 BorrowViolation(euv::ClosureCapture(_)) |
758 BorrowViolation(euv::OverloadedOperator) |
759 BorrowViolation(euv::AddrOf) |
760 BorrowViolation(euv::AutoRef) |
761 BorrowViolation(euv::AutoUnsafe) |
762 BorrowViolation(euv::RefBinding) |
763 BorrowViolation(euv::MatchDiscriminant) => {
764 "cannot borrow data mutably"
767 BorrowViolation(euv::ClosureInvocation) => {
772 BorrowViolation(euv::ForLoop) => {
777 let mut err = match cause {
778 mc::AliasableOther => {
780 self.tcx.sess, span, E0385,
781 "{} in an aliasable location", prefix)
783 mc::AliasableReason::UnaliasableImmutable => {
785 self.tcx.sess, span, E0386,
786 "{} in an immutable container", prefix)
788 mc::AliasableClosure(id) => {
789 let mut err = struct_span_err!(
790 self.tcx.sess, span, E0387,
791 "{} in a captured outer variable in an `Fn` closure", prefix);
792 if let BorrowViolation(euv::ClosureCapture(_)) = kind {
793 // The aliasability violation with closure captures can
794 // happen for nested closures, so we know the enclosing
795 // closure incorrectly accepts an `Fn` while it needs to
797 span_help!(&mut err, self.tcx.hir.span(id),
798 "consider changing this to accept closures that implement `FnMut`");
800 span_help!(&mut err, self.tcx.hir.span(id),
801 "consider changing this closure to take self by mutable reference");
805 mc::AliasableStatic |
806 mc::AliasableStaticMut => {
807 // This path cannot occur. It happens when we have an
808 // `&mut` or assignment to a static. But in the case
809 // of `static X`, we get a mutability violation first,
810 // and never get here. In the case of `static mut X`,
811 // that is unsafe and hence the aliasability error is
813 span_bug!(span, "aliasability violation for static `{}`", prefix)
815 mc::AliasableBorrowed => {
816 let mut e = struct_span_err!(
817 self.tcx.sess, span, E0389,
818 "{} in a `&` reference", prefix);
819 e.span_label(span, &"assignment into an immutable reference");
820 if let Some(nid) = cmt.get_arg_if_immutable(&self.tcx.hir) {
821 self.immutable_argument_should_be_mut(nid, &mut e);
828 err.help("closures behind references must be called via `&mut`");
833 /// Given a type, if it is an immutable reference, return a suggestion to make it mutable
834 fn suggest_mut_for_immutable(&self, pty: &hir::Ty) -> Option<String> {
835 // Check wether the argument is an immutable reference
836 if let hir::TyRptr(lifetime, hir::MutTy {
837 mutbl: hir::Mutability::MutImmutable,
840 // Account for existing lifetimes when generating the message
841 if !lifetime.is_elided() {
842 if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(ty.span) {
843 if let Ok(lifetime_snippet) = self.tcx.sess.codemap()
844 .span_to_snippet(lifetime.span) {
845 return Some(format!("use `&{} mut {}` here to make mutable",
850 } else if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(pty.span) {
851 if snippet.starts_with("&") {
852 return Some(format!("use `{}` here to make mutable",
853 snippet.replace("&", "&mut ")));
856 bug!("couldn't find a snippet for span: {:?}", pty.span);
862 fn immutable_argument_should_be_mut(&self, nid: ast::NodeId, db: &mut DiagnosticBuilder) {
863 let parent = self.tcx.hir.get_parent_node(nid);
864 let parent_node = self.tcx.hir.get(parent);
866 // The parent node is like a fn
867 if let Some(fn_like) = FnLikeNode::from_node(parent_node) {
868 // `nid`'s parent's `Body`
869 let fn_body = self.tcx.hir.body(fn_like.body());
870 // Get the position of `nid` in the arguments list
871 let arg_pos = fn_body.arguments.iter().position(|arg| arg.pat.id == nid);
872 if let Some(i) = arg_pos {
873 // The argument's `Ty`
874 let arg_ty = &fn_like.decl().inputs[i];
875 if let Some(msg) = self.suggest_mut_for_immutable(&arg_ty) {
876 db.span_label(arg_ty.span, &msg);
882 fn report_out_of_scope_escaping_closure_capture(&self,
883 err: &BckError<'tcx>,
886 let cmt_path_or_string = self.cmt_to_path_or_string(&err.cmt);
889 match self.tcx.sess.codemap().span_to_snippet(err.span) {
890 Ok(string) => format!("move {}", string),
891 Err(_) => format!("move |<args>| <body>")
894 struct_span_err!(self.tcx.sess, err.span, E0373,
895 "closure may outlive the current function, \
897 which is owned by the current function",
899 .span_label(capture_span,
900 &format!("{} is borrowed here",
902 .span_label(err.span,
903 &format!("may outlive borrowed value {}",
905 .span_suggestion(err.span,
906 &format!("to force the closure to take ownership of {} \
907 (and any other referenced variables), \
908 use the `move` keyword, as shown:",
914 fn region_end_span(&self, region: &'tcx ty::Region) -> Option<Span> {
916 ty::ReScope(scope) => {
917 match scope.span(&self.tcx.region_maps, &self.tcx.hir) {
930 pub fn note_and_explain_bckerr(&self, db: &mut DiagnosticBuilder, err: BckError<'tcx>) {
931 let error_span = err.span.clone();
933 err_mutbl => self.note_and_explain_mutbl_error(db, &err, &error_span),
934 err_out_of_scope(super_scope, sub_scope, cause) => {
935 let (value_kind, value_msg) = match err.cmt.cat {
936 mc::Categorization::Rvalue(..) =>
937 ("temporary value", "temporary value created here"),
939 ("borrowed value", "borrow occurs here")
942 let is_closure = match cause {
943 euv::ClosureCapture(s) => {
944 // The primary span starts out as the closure creation point.
945 // Change the primary span here to highlight the use of the variable
946 // in the closure, because it seems more natural. Highlight
947 // closure creation point as a secondary span.
948 match db.span.primary_span() {
950 db.span = MultiSpan::from_span(s);
951 db.span_label(primary, &format!("capture occurs here"));
952 db.span_label(s, &"does not live long enough");
959 db.span_label(error_span, &"does not live long enough");
964 let sub_span = self.region_end_span(sub_scope);
965 let super_span = self.region_end_span(super_scope);
967 match (sub_span, super_span) {
968 (Some(s1), Some(s2)) if s1 == s2 => {
970 db.span = MultiSpan::from_span(s1);
971 db.span_label(error_span, &value_msg);
972 let msg = match opt_loan_path(&err.cmt) {
973 None => value_kind.to_string(),
975 format!("`{}`", self.loan_path_to_string(&lp))
979 &format!("{} dropped here while still borrowed", msg));
981 db.span_label(s1, &format!("{} dropped before borrower", value_kind));
983 db.note("values in a scope are dropped in the opposite order \
986 (Some(s1), Some(s2)) if !is_closure => {
987 db.span = MultiSpan::from_span(s2);
988 db.span_label(error_span, &value_msg);
989 let msg = match opt_loan_path(&err.cmt) {
990 None => value_kind.to_string(),
992 format!("`{}`", self.loan_path_to_string(&lp))
995 db.span_label(s2, &format!("{} dropped here while still borrowed", msg));
996 db.span_label(s1, &format!("{} needs to live until here", value_kind));
1001 db.span_label(s, &format!("{} needs to live until here",
1005 self.tcx.note_and_explain_region(
1007 "borrowed value must be valid for ",
1014 db.span_label(s, &format!("{} only lives until here", value_kind));
1017 self.tcx.note_and_explain_region(
1019 "...but borrowed value is only valid for ",
1027 if let Some(_) = statement_scope_span(self.tcx, super_scope) {
1028 db.note("consider using a `let` binding to increase its lifetime");
1034 mc::Categorization::Rvalue(r, or) if r != or => {
1036 before rustc 1.16, this temporary lived longer - see issue #39283 \
1037 (https://github.com/rust-lang/rust/issues/39283)");
1043 err_borrowed_pointer_too_short(loan_scope, ptr_scope) => {
1044 let descr = match opt_loan_path(&err.cmt) {
1046 format!("`{}`", self.loan_path_to_string(&lp))
1048 None => self.cmt_to_string(&err.cmt),
1050 self.tcx.note_and_explain_region(
1052 &format!("{} would have to be valid for ",
1056 self.tcx.note_and_explain_region(
1058 &format!("...but {} is only valid for ", descr),
1065 fn note_and_explain_mutbl_error(&self, db: &mut DiagnosticBuilder, err: &BckError<'tcx>,
1066 error_span: &Span) {
1067 match err.cmt.note {
1068 mc::NoteClosureEnv(upvar_id) | mc::NoteUpvarRef(upvar_id) => {
1069 // If this is an `Fn` closure, it simply can't mutate upvars.
1070 // If it's an `FnMut` closure, the original variable was declared immutable.
1071 // We need to determine which is the case here.
1072 let kind = match err.cmt.upvar().unwrap().cat {
1073 Categorization::Upvar(mc::Upvar { kind, .. }) => kind,
1076 if kind == ty::ClosureKind::Fn {
1077 db.span_help(self.tcx.hir.span(upvar_id.closure_expr_id),
1078 "consider changing this closure to take \
1079 self by mutable reference");
1083 if let Categorization::Deref(..) = err.cmt.cat {
1084 db.span_label(*error_span, &"cannot borrow as mutable");
1085 if let Some(local_id) = err.cmt.get_arg_if_immutable(&self.tcx.hir) {
1086 self.immutable_argument_should_be_mut(local_id, db);
1087 } else if let Categorization::Deref(ref inner_cmt, ..) = err.cmt.cat {
1088 if let Categorization::Local(local_id) = inner_cmt.cat {
1089 self.immutable_argument_should_be_mut(local_id, db);
1092 } else if let Categorization::Local(local_id) = err.cmt.cat {
1093 let span = self.tcx.hir.span(local_id);
1094 if let Ok(snippet) = self.tcx.sess.codemap().span_to_snippet(span) {
1095 if snippet.starts_with("ref mut ") || snippet.starts_with("&mut ") {
1096 db.span_label(*error_span, &format!("cannot reborrow mutably"));
1097 db.span_label(*error_span, &format!("try removing `&mut` here"));
1099 if snippet.starts_with("ref ") {
1100 db.span_label(span, &format!("use `{}` here to make mutable",
1101 snippet.replace("ref ", "ref mut ")));
1102 } else if snippet != "self" {
1104 &format!("use `mut {}` here to make mutable",
1107 db.span_label(*error_span, &format!("cannot borrow mutably"));
1110 db.span_label(*error_span, &format!("cannot borrow mutably"));
1116 pub fn append_loan_path_to_string(&self,
1117 loan_path: &LoanPath<'tcx>,
1119 match loan_path.kind {
1120 LpUpvar(ty::UpvarId{ var_id: id, closure_expr_id: _ }) |
1122 out.push_str(&self.tcx.local_var_name_str(id));
1125 LpDowncast(ref lp_base, variant_def_id) => {
1127 self.append_loan_path_to_string(&lp_base, out);
1128 out.push_str(DOWNCAST_PRINTED_OPERATOR);
1129 out.push_str(&self.tcx.item_path_str(variant_def_id));
1133 LpExtend(ref lp_base, _, LpInterior(_, InteriorField(fname))) => {
1134 self.append_autoderefd_loan_path_to_string(&lp_base, out);
1136 mc::NamedField(fname) => {
1138 out.push_str(&fname.as_str());
1140 mc::PositionalField(idx) => {
1142 out.push_str(&idx.to_string());
1147 LpExtend(ref lp_base, _, LpInterior(_, InteriorElement(..))) => {
1148 self.append_autoderefd_loan_path_to_string(&lp_base, out);
1149 out.push_str("[..]");
1152 LpExtend(ref lp_base, _, LpDeref(_)) => {
1154 self.append_loan_path_to_string(&lp_base, out);
1159 pub fn append_autoderefd_loan_path_to_string(&self,
1160 loan_path: &LoanPath<'tcx>,
1162 match loan_path.kind {
1163 LpExtend(ref lp_base, _, LpDeref(_)) => {
1164 // For a path like `(*x).f` or `(*x)[3]`, autoderef
1165 // rules would normally allow users to omit the `*x`.
1166 // So just serialize such paths to `x.f` or x[3]` respectively.
1167 self.append_autoderefd_loan_path_to_string(&lp_base, out)
1170 LpDowncast(ref lp_base, variant_def_id) => {
1172 self.append_autoderefd_loan_path_to_string(&lp_base, out);
1174 out.push_str(&self.tcx.item_path_str(variant_def_id));
1178 LpVar(..) | LpUpvar(..) | LpExtend(.., LpInterior(..)) => {
1179 self.append_loan_path_to_string(loan_path, out)
1184 pub fn loan_path_to_string(&self, loan_path: &LoanPath<'tcx>) -> String {
1185 let mut result = String::new();
1186 self.append_loan_path_to_string(loan_path, &mut result);
1190 pub fn cmt_to_string(&self, cmt: &mc::cmt_<'tcx>) -> String {
1191 cmt.descriptive_string(self.tcx)
1194 pub fn cmt_to_path_or_string(&self, cmt: &mc::cmt<'tcx>) -> String {
1195 match opt_loan_path(cmt) {
1196 Some(lp) => format!("`{}`", self.loan_path_to_string(&lp)),
1197 None => self.cmt_to_string(cmt),
1202 fn statement_scope_span(tcx: TyCtxt, region: &ty::Region) -> Option<Span> {
1204 ty::ReScope(scope) => {
1205 match tcx.hir.find(scope.node_id(&tcx.region_maps)) {
1206 Some(hir_map::NodeStmt(stmt)) => Some(stmt.span),
1214 impl BitwiseOperator for LoanDataFlowOperator {
1216 fn join(&self, succ: usize, pred: usize) -> usize {
1217 succ | pred // loans from both preds are in scope
1221 impl DataFlowOperator for LoanDataFlowOperator {
1223 fn initial_value(&self) -> bool {
1224 false // no loans in scope by default
1228 impl<'tcx> fmt::Debug for InteriorKind {
1229 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1231 InteriorField(mc::NamedField(fld)) => write!(f, "{}", fld),
1232 InteriorField(mc::PositionalField(i)) => write!(f, "#{}", i),
1233 InteriorElement(..) => write!(f, "[]"),
1238 impl<'tcx> fmt::Debug for Loan<'tcx> {
1239 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1240 write!(f, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})",
1246 self.restricted_paths)
1250 impl<'tcx> fmt::Debug for LoanPath<'tcx> {
1251 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1254 write!(f, "$({})", ty::tls::with(|tcx| tcx.hir.node_to_string(id)))
1257 LpUpvar(ty::UpvarId{ var_id, closure_expr_id }) => {
1258 let s = ty::tls::with(|tcx| tcx.hir.node_to_string(var_id));
1259 write!(f, "$({} captured by id={})", s, closure_expr_id)
1262 LpDowncast(ref lp, variant_def_id) => {
1263 let variant_str = if variant_def_id.is_local() {
1264 ty::tls::with(|tcx| tcx.item_path_str(variant_def_id))
1266 format!("{:?}", variant_def_id)
1268 write!(f, "({:?}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
1271 LpExtend(ref lp, _, LpDeref(_)) => {
1272 write!(f, "{:?}.*", lp)
1275 LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
1276 write!(f, "{:?}.{:?}", lp, interior)
1282 impl<'tcx> fmt::Display for LoanPath<'tcx> {
1283 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
1286 write!(f, "$({})", ty::tls::with(|tcx| tcx.hir.node_to_user_string(id)))
1289 LpUpvar(ty::UpvarId{ var_id, closure_expr_id: _ }) => {
1290 let s = ty::tls::with(|tcx| tcx.hir.node_to_user_string(var_id));
1291 write!(f, "$({} captured by closure)", s)
1294 LpDowncast(ref lp, variant_def_id) => {
1295 let variant_str = if variant_def_id.is_local() {
1296 ty::tls::with(|tcx| tcx.item_path_str(variant_def_id))
1298 format!("{:?}", variant_def_id)
1300 write!(f, "({}{}{})", lp, DOWNCAST_PRINTED_OPERATOR, variant_str)
1303 LpExtend(ref lp, _, LpDeref(_)) => {
1304 write!(f, "{}.*", lp)
1307 LpExtend(ref lp, _, LpInterior(_, ref interior)) => {
1308 write!(f, "{}.{:?}", lp, interior)