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
::dep_graph
::DepNode
;
24 use rustc
::hir
::map
as hir_map
;
25 use rustc
::hir
::map
::blocks
::FnParts
;
27 use rustc
::middle
::dataflow
::DataFlowContext
;
28 use rustc
::middle
::dataflow
::BitwiseOperator
;
29 use rustc
::middle
::dataflow
::DataFlowOperator
;
30 use rustc
::middle
::dataflow
::KillFrom
;
31 use rustc
::hir
::def_id
::DefId
;
32 use rustc
::middle
::expr_use_visitor
as euv
;
33 use rustc
::middle
::free_region
::FreeRegionMap
;
34 use rustc
::middle
::mem_categorization
as mc
;
35 use rustc
::middle
::mem_categorization
::Categorization
;
36 use rustc
::middle
::region
;
37 use rustc
::ty
::{self, Ty, TyCtxt}
;
43 use syntax
::attr
::AttrMetaMethods
;
44 use syntax
::codemap
::Span
;
45 use syntax
::errors
::DiagnosticBuilder
;
48 use rustc
::hir
::{FnDecl, Block}
;
49 use rustc
::hir
::intravisit
;
50 use rustc
::hir
::intravisit
::{Visitor, FnKind}
;
52 use rustc
::mir
::mir_map
::MirMap
;
62 #[derive(Clone, Copy)]
63 pub struct LoanDataFlowOperator
;
65 pub type LoanDataFlow
<'a
, 'tcx
> = DataFlowContext
<'a
, 'tcx
, LoanDataFlowOperator
>;
67 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for BorrowckCtxt
<'a
, 'tcx
> {
68 fn visit_fn(&mut self, fk
: FnKind
<'v
>, fd
: &'v FnDecl
,
69 b
: &'v Block
, s
: Span
, id
: ast
::NodeId
) {
72 FnKind
::Method(..) => {
73 self.with_temp_region_map(id
, |this
| {
74 borrowck_fn(this
, fk
, fd
, b
, s
, id
, fk
.attrs())
78 FnKind
::Closure(..) => {
79 borrowck_fn(self, fk
, fd
, b
, s
, id
, fk
.attrs());
84 fn visit_item(&mut self, item
: &hir
::Item
) {
85 borrowck_item(self, item
);
88 fn visit_trait_item(&mut self, ti
: &hir
::TraitItem
) {
89 if let hir
::ConstTraitItem(_
, Some(ref expr
)) = ti
.node
{
90 gather_loans
::gather_loans_in_static_initializer(self, &expr
);
92 intravisit
::walk_trait_item(self, ti
);
95 fn visit_impl_item(&mut self, ii
: &hir
::ImplItem
) {
96 if let hir
::ImplItemKind
::Const(_
, ref expr
) = ii
.node
{
97 gather_loans
::gather_loans_in_static_initializer(self, &expr
);
99 intravisit
::walk_impl_item(self, ii
);
103 pub fn check_crate
<'tcx
>(tcx
: &TyCtxt
<'tcx
>, mir_map
: &MirMap
<'tcx
>) {
104 let mut bccx
= BorrowckCtxt
{
106 mir_map
: Some(mir_map
),
107 free_region_map
: FreeRegionMap
::new(),
109 loaned_paths_same
: 0,
116 tcx
.visit_all_items_in_krate(DepNode
::BorrowCheck
, &mut bccx
);
118 if tcx
.sess
.borrowck_stats() {
119 println
!("--- borrowck stats ---");
120 println
!("paths requiring guarantees: {}",
121 bccx
.stats
.guaranteed_paths
);
122 println
!("paths requiring loans : {}",
123 make_stat(&bccx
, bccx
.stats
.loaned_paths_same
));
124 println
!("paths requiring imm loans : {}",
125 make_stat(&bccx
, bccx
.stats
.loaned_paths_imm
));
126 println
!("stable paths : {}",
127 make_stat(&bccx
, bccx
.stats
.stable_paths
));
130 fn make_stat(bccx
: &BorrowckCtxt
, stat
: usize) -> String
{
131 let total
= bccx
.stats
.guaranteed_paths
as f64;
132 let perc
= if total
== 0.0 { 0.0 }
else { stat as f64 * 100.0 / total }
;
133 format
!("{} ({:.0}%)", stat
, perc
)
137 fn borrowck_item(this
: &mut BorrowckCtxt
, item
: &hir
::Item
) {
138 // Gather loans for items. Note that we don't need
139 // to check loans for single expressions. The check
140 // loan step is intended for things that have a data
141 // flow dependent conditions.
143 hir
::ItemStatic(_
, _
, ref ex
) |
144 hir
::ItemConst(_
, ref ex
) => {
145 gather_loans
::gather_loans_in_static_initializer(this
, &ex
);
150 intravisit
::walk_item(this
, item
);
153 /// Collection of conclusions determined via borrow checker analyses.
154 pub struct AnalysisData
<'a
, 'tcx
: 'a
> {
155 pub all_loans
: Vec
<Loan
<'tcx
>>,
156 pub loans
: DataFlowContext
<'a
, 'tcx
, LoanDataFlowOperator
>,
157 pub move_data
: move_data
::FlowedMoveData
<'a
, 'tcx
>,
160 fn borrowck_fn(this
: &mut BorrowckCtxt
,
166 attributes
: &[ast
::Attribute
]) {
167 debug
!("borrowck_fn(id={})", id
);
169 if attributes
.iter().any(|item
| item
.check_name("rustc_mir_borrowck")) {
170 let mir
= this
.mir_map
.unwrap().map
.get(&id
).unwrap();
171 this
.with_temp_region_map(id
, |this
| {
172 mir
::borrowck_mir(this
, fk
, decl
, mir
, body
, sp
, id
, attributes
)
176 let cfg
= cfg
::CFG
::new(this
.tcx
, body
);
177 let AnalysisData
{ all_loans
,
179 move_data
: flowed_moves
} =
180 build_borrowck_dataflow_data(this
, fk
, decl
, &cfg
, body
, sp
, id
);
182 move_data
::fragments
::instrument_move_fragments(&flowed_moves
.move_data
,
186 move_data
::fragments
::build_unfragmented_map(this
,
187 &flowed_moves
.move_data
,
190 check_loans
::check_loans(this
,
198 intravisit
::walk_fn(this
, fk
, decl
, body
, sp
);
201 fn build_borrowck_dataflow_data
<'a
, 'tcx
>(this
: &mut BorrowckCtxt
<'a
, 'tcx
>,
208 -> AnalysisData
<'a
, 'tcx
>
210 // Check the body of fn items.
212 let id_range
= intravisit
::compute_id_range_for_fn_body(fk
, decl
, body
, sp
, id
);
213 let (all_loans
, move_data
) =
214 gather_loans
::gather_loans_in_fn(this
, id
, decl
, body
);
217 DataFlowContext
::new(this
.tcx
,
221 LoanDataFlowOperator
,
224 for (loan_idx
, loan
) in all_loans
.iter().enumerate() {
225 loan_dfcx
.add_gen(loan
.gen_scope
.node_id(&tcx
.region_maps
), loan_idx
);
226 loan_dfcx
.add_kill(KillFrom
::ScopeEnd
,
227 loan
.kill_scope
.node_id(&tcx
.region_maps
), loan_idx
);
229 loan_dfcx
.add_kills_from_flow_exits(cfg
);
230 loan_dfcx
.propagate(cfg
, body
);
232 let flowed_moves
= move_data
::FlowedMoveData
::new(move_data
,
239 AnalysisData
{ all_loans
: all_loans
,
241 move_data
:flowed_moves
}
244 /// Accessor for introspective clients inspecting `AnalysisData` and
245 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
246 pub fn build_borrowck_dataflow_data_for_fn
<'a
, 'tcx
>(
247 tcx
: &'a TyCtxt
<'tcx
>,
248 mir_map
: Option
<&'a MirMap
<'tcx
>>,
249 fn_parts
: FnParts
<'a
>,
251 -> (BorrowckCtxt
<'a
, 'tcx
>, AnalysisData
<'a
, 'tcx
>)
254 let mut bccx
= BorrowckCtxt
{
257 free_region_map
: FreeRegionMap
::new(),
259 loaned_paths_same
: 0,
266 let dataflow_data
= build_borrowck_dataflow_data(&mut bccx
,
274 (bccx
, dataflow_data
)
277 // ----------------------------------------------------------------------
280 pub struct BorrowckCtxt
<'a
, 'tcx
: 'a
> {
281 tcx
: &'a TyCtxt
<'tcx
>,
283 // Hacky. As we visit various fns, we have to load up the
284 // free-region map for each one. This map is computed by during
285 // typeck for each fn item and stored -- closures just use the map
286 // from the fn item that encloses them. Since we walk the fns in
287 // order, we basically just overwrite this field as we enter a fn
288 // item and restore it afterwards in a stack-like fashion. Then
289 // the borrow checking code can assume that `free_region_map` is
290 // always the correct map for the current fn. Feels like it'd be
291 // better to just recompute this, rather than store it, but it's a
292 // bit of a pain to factor that code out at the moment.
293 free_region_map
: FreeRegionMap
,
298 // NodeId to MIR mapping (for methods that carry the #[rustc_mir] attribute).
299 mir_map
: Option
<&'a MirMap
<'tcx
>>,
304 loaned_paths_same
: usize,
305 loaned_paths_imm
: usize,
307 guaranteed_paths
: usize
310 pub type BckResult
<'tcx
, T
> = Result
<T
, BckError
<'tcx
>>;
312 ///////////////////////////////////////////////////////////////////////////
313 // Loans and loan paths
315 /// Record of a loan that was issued.
316 pub struct Loan
<'tcx
> {
318 loan_path
: Rc
<LoanPath
<'tcx
>>,
319 kind
: ty
::BorrowKind
,
320 restricted_paths
: Vec
<Rc
<LoanPath
<'tcx
>>>,
322 /// gen_scope indicates where loan is introduced. Typically the
323 /// loan is introduced at the point of the borrow, but in some
324 /// cases, notably method arguments, the loan may be introduced
325 /// only later, once it comes into scope. See also
326 /// `GatherLoanCtxt::compute_gen_scope`.
327 gen_scope
: region
::CodeExtent
,
329 /// kill_scope indicates when the loan goes out of scope. This is
330 /// either when the lifetime expires or when the local variable
331 /// which roots the loan-path goes out of scope, whichever happens
332 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
333 kill_scope
: region
::CodeExtent
,
335 cause
: euv
::LoanCause
,
338 impl<'tcx
> Loan
<'tcx
> {
339 pub fn loan_path(&self) -> Rc
<LoanPath
<'tcx
>> {
340 self.loan_path
.clone()
345 pub struct LoanPath
<'tcx
> {
346 kind
: LoanPathKind
<'tcx
>,
350 impl<'tcx
> PartialEq
for LoanPath
<'tcx
> {
351 fn eq(&self, that
: &LoanPath
<'tcx
>) -> bool
{
352 let r
= self.kind
== that
.kind
;
353 debug_assert
!(self.ty
== that
.ty
|| !r
,
354 "Somehow loan paths are equal though their tys are not.");
359 #[derive(PartialEq, Eq, Hash, Debug)]
360 pub enum LoanPathKind
<'tcx
> {
361 LpVar(ast
::NodeId
), // `x` in README.md
362 LpUpvar(ty
::UpvarId
), // `x` captured by-value into closure
363 LpDowncast(Rc
<LoanPath
<'tcx
>>, DefId
), // `x` downcast to particular enum variant
364 LpExtend(Rc
<LoanPath
<'tcx
>>, mc
::MutabilityCategory
, LoanPathElem
)
367 impl<'tcx
> LoanPath
<'tcx
> {
368 fn new(kind
: LoanPathKind
<'tcx
>, ty
: ty
::Ty
<'tcx
>) -> LoanPath
<'tcx
> {
369 LoanPath { kind: kind, ty: ty }
372 fn to_type(&self) -> ty
::Ty
<'tcx
> { self.ty }
375 // FIXME (pnkfelix): See discussion here
376 // https://github.com/pnkfelix/rust/commit/
377 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
378 const DOWNCAST_PRINTED_OPERATOR
: &'
static str = " as ";
380 // A local, "cleaned" version of `mc::InteriorKind` that drops
381 // information that is not relevant to loan-path analysis. (In
382 // particular, the distinction between how precisely an array-element
383 // is tracked is irrelevant here.)
384 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
385 pub enum InteriorKind
{
386 InteriorField(mc
::FieldName
),
387 InteriorElement(mc
::ElementKind
),
390 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
391 impl ToInteriorKind
for mc
::InteriorKind
{
392 fn cleaned(self) -> InteriorKind
{
394 mc
::InteriorField(name
) => InteriorField(name
),
395 mc
::InteriorElement(_
, elem_kind
) => InteriorElement(elem_kind
),
401 // - a pointer dereference (`*LV` in README.md)
402 // - a field reference, with an optional definition of the containing
403 // enum variant (`LV.f` in README.md)
404 // `DefId` is present when the field is part of struct that is in
405 // a variant of an enum. For instance in:
406 // `enum E { X { foo: u32 }, Y { foo: u32 }}`
407 // each `foo` is qualified by the definitition id of the variant (`X` or `Y`).
408 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
409 pub enum LoanPathElem
{
410 LpDeref(mc
::PointerKind
),
411 LpInterior(Option
<DefId
>, InteriorKind
),
414 pub fn closure_to_block(closure_id
: ast
::NodeId
,
415 tcx
: &TyCtxt
) -> ast
::NodeId
{
416 match tcx
.map
.get(closure_id
) {
417 hir_map
::NodeExpr(expr
) => match expr
.node
{
418 hir
::ExprClosure(_
, _
, ref block
) => {
422 bug
!("encountered non-closure id: {}", closure_id
)
425 _
=> bug
!("encountered non-expr id: {}", closure_id
)
429 impl<'tcx
> LoanPath
<'tcx
> {
430 pub fn kill_scope(&self, tcx
: &TyCtxt
<'tcx
>) -> region
::CodeExtent
{
432 LpVar(local_id
) => tcx
.region_maps
.var_scope(local_id
),
433 LpUpvar(upvar_id
) => {
434 let block_id
= closure_to_block(upvar_id
.closure_expr_id
, tcx
);
435 tcx
.region_maps
.node_extent(block_id
)
437 LpDowncast(ref base
, _
) |
438 LpExtend(ref base
, _
, _
) => base
.kill_scope(tcx
),
442 fn has_fork(&self, other
: &LoanPath
<'tcx
>) -> bool
{
443 match (&self.kind
, &other
.kind
) {
444 (&LpExtend(ref base
, _
, LpInterior(opt_variant_id
, id
)),
445 &LpExtend(ref base2
, _
, LpInterior(opt_variant_id2
, id2
))) =>
446 if id
== id2
&& opt_variant_id
== opt_variant_id2
{
447 base
.has_fork(&base2
)
451 (&LpExtend(ref base
, _
, LpDeref(_
)), _
) => base
.has_fork(other
),
452 (_
, &LpExtend(ref base
, _
, LpDeref(_
))) => self.has_fork(&base
),
457 fn depth(&self) -> usize {
459 LpExtend(ref base
, _
, LpDeref(_
)) => base
.depth(),
460 LpExtend(ref base
, _
, LpInterior(_
, _
)) => base
.depth() + 1,
465 fn common(&self, other
: &LoanPath
<'tcx
>) -> Option
<LoanPath
<'tcx
>> {
466 match (&self.kind
, &other
.kind
) {
467 (&LpExtend(ref base
, a
, LpInterior(opt_variant_id
, id
)),
468 &LpExtend(ref base2
, _
, LpInterior(opt_variant_id2
, id2
))) => {
469 if id
== id2
&& opt_variant_id
== opt_variant_id2
{
470 base
.common(&base2
).map(|x
| {
472 if base
.depth() == xd
&& base2
.depth() == xd
{
473 assert_eq
!(base
.ty
, base2
.ty
);
474 assert_eq
!(self.ty
, other
.ty
);
476 kind
: LpExtend(Rc
::new(x
), a
, LpInterior(opt_variant_id
, id
)),
487 (&LpExtend(ref base
, _
, LpDeref(_
)), _
) => base
.common(other
),
488 (_
, &LpExtend(ref other
, _
, LpDeref(_
))) => self.common(&other
),
489 (&LpVar(id
), &LpVar(id2
)) => {
491 assert_eq
!(self.ty
, other
.ty
);
492 Some(LoanPath { kind: LpVar(id), ty: self.ty }
)
497 (&LpUpvar(id
), &LpUpvar(id2
)) => {
499 assert_eq
!(self.ty
, other
.ty
);
500 Some(LoanPath { kind: LpUpvar(id), ty: self.ty }
)
510 pub fn opt_loan_path
<'tcx
>(cmt
: &mc
::cmt
<'tcx
>) -> Option
<Rc
<LoanPath
<'tcx
>>> {
511 //! Computes the `LoanPath` (if any) for a `cmt`.
512 //! Note that this logic is somewhat duplicated in
513 //! the method `compute()` found in `gather_loans::restrictions`,
514 //! which allows it to share common loan path pieces as it
515 //! traverses the CMT.
517 let new_lp
= |v
: LoanPathKind
<'tcx
>| Rc
::new(LoanPath
::new(v
, cmt
.ty
));
520 Categorization
::Rvalue(..) |
521 Categorization
::StaticItem
=> {
525 Categorization
::Local(id
) => {
526 Some(new_lp(LpVar(id
)))
529 Categorization
::Upvar(mc
::Upvar { id, .. }
) => {
530 Some(new_lp(LpUpvar(id
)))
533 Categorization
::Deref(ref cmt_base
, _
, pk
) => {
534 opt_loan_path(cmt_base
).map(|lp
| {
535 new_lp(LpExtend(lp
, cmt
.mutbl
, LpDeref(pk
)))
539 Categorization
::Interior(ref cmt_base
, ik
) => {
540 opt_loan_path(cmt_base
).map(|lp
| {
541 let opt_variant_id
= match cmt_base
.cat
{
542 Categorization
::Downcast(_
, did
) => Some(did
),
545 new_lp(LpExtend(lp
, cmt
.mutbl
, LpInterior(opt_variant_id
, ik
.cleaned())))
549 Categorization
::Downcast(ref cmt_base
, variant_def_id
) =>
550 opt_loan_path(cmt_base
)
552 new_lp(LpDowncast(lp
, variant_def_id
))
558 ///////////////////////////////////////////////////////////////////////////
561 // Errors that can occur
563 pub enum bckerr_code
{
565 err_out_of_scope(ty
::Region
, ty
::Region
), // superscope, subscope
566 err_borrowed_pointer_too_short(ty
::Region
, ty
::Region
), // loan, ptr
569 // Combination of an error code and the categorization of the expression
572 pub struct BckError
<'tcx
> {
574 cause
: AliasableViolationKind
,
579 #[derive(Copy, Clone, Debug, PartialEq)]
580 pub enum AliasableViolationKind
{
582 BorrowViolation(euv
::LoanCause
)
585 #[derive(Copy, Clone, Debug)]
586 pub enum MovedValueUseKind
{
591 ///////////////////////////////////////////////////////////////////////////
594 impl<'a
, 'tcx
> BorrowckCtxt
<'a
, 'tcx
> {
595 fn with_temp_region_map
<F
>(&mut self, id
: ast
::NodeId
, f
: F
)
596 where F
: for <'b
> FnOnce(&'b
mut BorrowckCtxt
<'a
, 'tcx
>)
598 let new_free_region_map
= self.tcx
.free_region_map(id
);
599 let old_free_region_map
= mem
::replace(&mut self.free_region_map
, new_free_region_map
);
601 self.free_region_map
= old_free_region_map
;
604 pub fn is_subregion_of(&self, r_sub
: ty
::Region
, r_sup
: ty
::Region
)
607 self.free_region_map
.is_subregion_of(self.tcx
, r_sub
, r_sup
)
610 pub fn report(&self, err
: BckError
<'tcx
>) {
611 // Catch and handle some particular cases.
612 match (&err
.code
, &err
.cause
) {
613 (&err_out_of_scope(ty
::ReScope(_
), ty
::ReStatic
),
614 &BorrowViolation(euv
::ClosureCapture(span
))) |
615 (&err_out_of_scope(ty
::ReScope(_
), ty
::ReFree(..)),
616 &BorrowViolation(euv
::ClosureCapture(span
))) => {
617 return self.report_out_of_scope_escaping_closure_capture(&err
, span
);
623 let mut db
= self.struct_span_err(
625 &self.bckerr_to_string(&err
));
626 self.note_and_explain_bckerr(&mut db
, err
);
630 pub fn report_use_of_moved_value
<'b
>(&self,
632 use_kind
: MovedValueUseKind
,
634 the_move
: &move_data
::Move
,
635 moved_lp
: &LoanPath
<'tcx
>,
636 param_env
: &ty
::ParameterEnvironment
<'b
,'tcx
>) {
637 let verb
= match use_kind
{
639 MovedInCapture
=> "capture",
642 let (ol
, moved_lp_msg
, mut err
) = match the_move
.kind
{
643 move_data
::Declared
=> {
644 let err
= struct_span_err
!(
645 self.tcx
.sess
, use_span
, E0381
,
646 "{} of possibly uninitialized variable: `{}`",
648 self.loan_path_to_string(lp
));
650 (self.loan_path_to_string(moved_lp
),
655 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
656 // normally generate a rather confusing message:
658 // error: use of moved value: `x.b`
659 // note: `x.a` moved here...
661 // What we want to do instead is get the 'common ancestor' of the two moves and
662 // use that for most of the message instead, giving is something like this:
664 // error: use of moved value: `x`
665 // note: `x` moved here (through moving `x.a`)...
667 let common
= moved_lp
.common(lp
);
668 let has_common
= common
.is_some();
669 let has_fork
= moved_lp
.has_fork(lp
);
670 let (nl
, ol
, moved_lp_msg
) =
671 if has_fork
&& has_common
{
672 let nl
= self.loan_path_to_string(&common
.unwrap());
674 let moved_lp_msg
= format
!(" (through moving `{}`)",
675 self.loan_path_to_string(moved_lp
));
676 (nl
, ol
, moved_lp_msg
)
678 (self.loan_path_to_string(lp
),
679 self.loan_path_to_string(moved_lp
),
683 let partial
= moved_lp
.depth() > lp
.depth();
684 let msg
= if !has_fork
&& partial { "partially " }
685 else if has_fork
&& !has_common { "collaterally "}
687 let err
= struct_span_err
!(
688 self.tcx
.sess
, use_span
, E0382
,
689 "{} of {}moved value: `{}`",
691 (ol
, moved_lp_msg
, err
)
695 match the_move
.kind
{
696 move_data
::Declared
=> {}
698 move_data
::MoveExpr
=> {
699 let (expr_ty
, expr_span
) = match self.tcx
702 Some(hir_map
::NodeExpr(expr
)) => {
703 (self.tcx
.expr_ty_adjusted(&expr
), expr
.span
)
706 bug
!("MoveExpr({}) maps to {:?}, not Expr",
711 let (suggestion
, _
) =
712 move_suggestion(param_env
, expr_span
, expr_ty
, ("moved by default", ""));
713 // If the two spans are the same, it's because the expression will be evaluated
714 // multiple times. Avoid printing the same span and adjust the wording so it makes
715 // more sense that it's from multiple evalutations.
716 if expr_span
== use_span
{
718 &format
!("`{}` was previously moved here{} because it has type `{}`, \
727 &format
!("`{}` moved here{} because it has type `{}`, which is {}",
735 move_data
::MovePat
=> {
736 let pat_ty
= self.tcx
.node_id_to_type(the_move
.id
);
737 let span
= self.tcx
.map
.span(the_move
.id
);
739 &format
!("`{}` moved here{} because it has type `{}`, \
740 which is moved by default",
744 match self.tcx
.sess
.codemap().span_to_snippet(span
) {
748 &format
!("if you would like to borrow the value instead, \
749 use a `ref` binding as shown:"),
750 format
!("ref {}", string
));
753 err
.fileline_help(span
,
754 "use `ref` to override");
759 move_data
::Captured
=> {
760 let (expr_ty
, expr_span
) = match self.tcx
763 Some(hir_map
::NodeExpr(expr
)) => {
764 (self.tcx
.expr_ty_adjusted(&expr
), expr
.span
)
767 bug
!("Captured({}) maps to {:?}, not Expr",
772 let (suggestion
, help
) =
773 move_suggestion(param_env
,
777 "make a copy and capture that instead to override"));
780 &format
!("`{}` moved into closure environment here{} because it \
781 has type `{}`, which is {}",
786 err
.fileline_help(expr_span
, help
);
791 fn move_suggestion
<'a
,'tcx
>(param_env
: &ty
::ParameterEnvironment
<'a
,'tcx
>,
794 default_msgs
: (&'
static str, &'
static str))
795 -> (&'
static str, &'
static str) {
798 if ty
.moves_by_default(param_env
, span
) {
800 "perhaps you meant to use `clone()`?")
809 pub fn report_partial_reinitialization_of_uninitialized_structure(
812 lp
: &LoanPath
<'tcx
>) {
814 self.tcx
.sess
, span
, E0383
,
815 "partial reinitialization of uninitialized structure `{}`",
816 self.loan_path_to_string(lp
));
819 pub fn report_reassigned_immutable_variable(&self,
823 &move_data
::Assignment
) {
825 self.tcx
.sess
, span
, E0384
,
826 "re-assignment of immutable variable `{}`",
827 self.loan_path_to_string(lp
))
828 .span_note(assign
.span
, "prior assignment occurs here")
832 pub fn span_err(&self, s
: Span
, m
: &str) {
833 self.tcx
.sess
.span_err(s
, m
);
836 pub fn struct_span_err(&self, s
: Span
, m
: &str) -> DiagnosticBuilder
<'a
> {
837 self.tcx
.sess
.struct_span_err(s
, m
)
840 pub fn struct_span_err_with_code(&self,
844 -> DiagnosticBuilder
<'a
> {
845 self.tcx
.sess
.struct_span_err_with_code(s
, msg
, code
)
848 pub fn span_err_with_code(&self, s
: Span
, msg
: &str, code
: &str) {
849 self.tcx
.sess
.span_err_with_code(s
, msg
, code
);
852 pub fn bckerr_to_string(&self, err
: &BckError
<'tcx
>) -> String
{
855 let descr
= match err
.cmt
.note
{
856 mc
::NoteClosureEnv(_
) | mc
::NoteUpvarRef(_
) => {
857 self.cmt_to_string(&err
.cmt
)
859 _
=> match opt_loan_path(&err
.cmt
) {
862 err
.cmt
.mutbl
.to_user_str(),
863 self.cmt_to_string(&err
.cmt
))
866 format
!("{} {} `{}`",
867 err
.cmt
.mutbl
.to_user_str(),
868 self.cmt_to_string(&err
.cmt
),
869 self.loan_path_to_string(&lp
))
875 MutabilityViolation
=> {
876 format
!("cannot assign to {}", descr
)
878 BorrowViolation(euv
::ClosureCapture(_
)) => {
879 format
!("closure cannot assign to {}", descr
)
881 BorrowViolation(euv
::OverloadedOperator
) |
882 BorrowViolation(euv
::AddrOf
) |
883 BorrowViolation(euv
::RefBinding
) |
884 BorrowViolation(euv
::AutoRef
) |
885 BorrowViolation(euv
::AutoUnsafe
) |
886 BorrowViolation(euv
::ForLoop
) |
887 BorrowViolation(euv
::MatchDiscriminant
) => {
888 format
!("cannot borrow {} as mutable", descr
)
890 BorrowViolation(euv
::ClosureInvocation
) => {
892 "err_mutbl with a closure invocation");
896 err_out_of_scope(..) => {
897 let msg
= match opt_loan_path(&err
.cmt
) {
898 None
=> "borrowed value".to_string(),
900 format
!("`{}`", self.loan_path_to_string(&lp
))
903 format
!("{} does not live long enough", msg
)
905 err_borrowed_pointer_too_short(..) => {
906 let descr
= self.cmt_to_path_or_string(&err
.cmt
);
907 format
!("lifetime of {} is too short to guarantee \
908 its contents can be safely reborrowed",
914 pub fn report_aliasability_violation(&self,
916 kind
: AliasableViolationKind
,
917 cause
: mc
::AliasableReason
) {
918 let mut is_closure
= false;
919 let prefix
= match kind
{
920 MutabilityViolation
=> {
921 "cannot assign to data"
923 BorrowViolation(euv
::ClosureCapture(_
)) |
924 BorrowViolation(euv
::OverloadedOperator
) |
925 BorrowViolation(euv
::AddrOf
) |
926 BorrowViolation(euv
::AutoRef
) |
927 BorrowViolation(euv
::AutoUnsafe
) |
928 BorrowViolation(euv
::RefBinding
) |
929 BorrowViolation(euv
::MatchDiscriminant
) => {
930 "cannot borrow data mutably"
933 BorrowViolation(euv
::ClosureInvocation
) => {
938 BorrowViolation(euv
::ForLoop
) => {
943 let mut err
= match cause
{
944 mc
::AliasableOther
=> {
946 self.tcx
.sess
, span
, E0385
,
947 "{} in an aliasable location", prefix
)
949 mc
::AliasableReason
::UnaliasableImmutable
=> {
951 self.tcx
.sess
, span
, E0386
,
952 "{} in an immutable container", prefix
)
954 mc
::AliasableClosure(id
) => {
955 let mut err
= struct_span_err
!(
956 self.tcx
.sess
, span
, E0387
,
957 "{} in a captured outer variable in an `Fn` closure", prefix
);
958 if let BorrowViolation(euv
::ClosureCapture(_
)) = kind
{
959 // The aliasability violation with closure captures can
960 // happen for nested closures, so we know the enclosing
961 // closure incorrectly accepts an `Fn` while it needs to
963 span_help
!(&mut err
, self.tcx
.map
.span(id
),
964 "consider changing this to accept closures that implement `FnMut`");
966 span_help
!(&mut err
, self.tcx
.map
.span(id
),
967 "consider changing this closure to take self by mutable reference");
971 mc
::AliasableStatic
|
972 mc
::AliasableStaticMut
=> {
974 self.tcx
.sess
, span
, E0388
,
975 "{} in a static location", prefix
)
977 mc
::AliasableBorrowed
=> {
979 self.tcx
.sess
, span
, E0389
,
980 "{} in a `&` reference", prefix
)
985 err
.fileline_help(span
,
986 "closures behind references must be called via `&mut`");
991 fn report_out_of_scope_escaping_closure_capture(&self,
992 err
: &BckError
<'tcx
>,
995 let cmt_path_or_string
= self.cmt_to_path_or_string(&err
.cmt
);
998 match self.tcx
.sess
.codemap().span_to_snippet(err
.span
) {
999 Ok(string
) => format
!("move {}", string
),
1000 Err(_
) => format
!("move |<args>| <body>")
1003 struct_span_err
!(self.tcx
.sess
, err
.span
, E0373
,
1004 "closure may outlive the current function, \
1005 but it borrows {}, \
1006 which is owned by the current function",
1008 .span_note(capture_span
,
1009 &format
!("{} is borrowed here",
1010 cmt_path_or_string
))
1011 .span_suggestion(err
.span
,
1012 &format
!("to force the closure to take ownership of {} \
1013 (and any other referenced variables), \
1014 use the `move` keyword, as shown:",
1015 cmt_path_or_string
),
1020 pub fn note_and_explain_bckerr(&self, db
: &mut DiagnosticBuilder
, err
: BckError
<'tcx
>) {
1021 let code
= err
.code
;
1024 match err
.cmt
.note
{
1025 mc
::NoteClosureEnv(upvar_id
) | mc
::NoteUpvarRef(upvar_id
) => {
1026 // If this is an `Fn` closure, it simply can't mutate upvars.
1027 // If it's an `FnMut` closure, the original variable was declared immutable.
1028 // We need to determine which is the case here.
1029 let kind
= match err
.cmt
.upvar().unwrap().cat
{
1030 Categorization
::Upvar(mc
::Upvar { kind, .. }
) => kind
,
1033 if kind
== ty
::ClosureKind
::Fn
{
1035 self.tcx
.map
.span(upvar_id
.closure_expr_id
),
1036 "consider changing this closure to take \
1037 self by mutable reference");
1041 if let Categorization
::Local(local_id
) = err
.cmt
.cat
{
1042 let span
= self.tcx
.map
.span(local_id
);
1043 if let Ok(snippet
) = self.tcx
.sess
.codemap().span_to_snippet(span
) {
1046 &format
!("to make the {} mutable, use `mut` as shown:",
1047 self.cmt_to_string(&err
.cmt
)),
1048 format
!("mut {}", snippet
));
1055 err_out_of_scope(super_scope
, sub_scope
) => {
1056 self.tcx
.note_and_explain_region(
1058 "reference must be valid for ",
1061 self.tcx
.note_and_explain_region(
1063 "...but borrowed value is only valid for ",
1066 if let Some(span
) = statement_scope_span(self.tcx
, super_scope
) {
1068 "consider using a `let` binding to increase its lifetime");
1072 err_borrowed_pointer_too_short(loan_scope
, ptr_scope
) => {
1073 let descr
= match opt_loan_path(&err
.cmt
) {
1075 format
!("`{}`", self.loan_path_to_string(&lp
))
1077 None
=> self.cmt_to_string(&err
.cmt
),
1079 self.tcx
.note_and_explain_region(
1081 &format
!("{} would have to be valid for ",
1085 self.tcx
.note_and_explain_region(
1087 &format
!("...but {} is only valid for ", descr
),
1094 pub fn append_loan_path_to_string(&self,
1095 loan_path
: &LoanPath
<'tcx
>,
1097 match loan_path
.kind
{
1098 LpUpvar(ty
::UpvarId{ var_id: id, closure_expr_id: _ }
) |
1100 out
.push_str(&self.tcx
.local_var_name_str(id
));
1103 LpDowncast(ref lp_base
, variant_def_id
) => {
1105 self.append_loan_path_to_string(&lp_base
, out
);
1106 out
.push_str(DOWNCAST_PRINTED_OPERATOR
);
1107 out
.push_str(&self.tcx
.item_path_str(variant_def_id
));
1112 LpExtend(ref lp_base
, _
, LpInterior(_
, InteriorField(fname
))) => {
1113 self.append_autoderefd_loan_path_to_string(&lp_base
, out
);
1115 mc
::NamedField(fname
) => {
1117 out
.push_str(&fname
.as_str());
1119 mc
::PositionalField(idx
) => {
1121 out
.push_str(&idx
.to_string());
1126 LpExtend(ref lp_base
, _
, LpInterior(_
, InteriorElement(..))) => {
1127 self.append_autoderefd_loan_path_to_string(&lp_base
, out
);
1128 out
.push_str("[..]");
1131 LpExtend(ref lp_base
, _
, LpDeref(_
)) => {
1133 self.append_loan_path_to_string(&lp_base
, out
);
1138 pub fn append_autoderefd_loan_path_to_string(&self,
1139 loan_path
: &LoanPath
<'tcx
>,
1141 match loan_path
.kind
{
1142 LpExtend(ref lp_base
, _
, LpDeref(_
)) => {
1143 // For a path like `(*x).f` or `(*x)[3]`, autoderef
1144 // rules would normally allow users to omit the `*x`.
1145 // So just serialize such paths to `x.f` or x[3]` respectively.
1146 self.append_autoderefd_loan_path_to_string(&lp_base
, out
)
1149 LpDowncast(ref lp_base
, variant_def_id
) => {
1151 self.append_autoderefd_loan_path_to_string(&lp_base
, out
);
1153 out
.push_str(&self.tcx
.item_path_str(variant_def_id
));
1157 LpVar(..) | LpUpvar(..) | LpExtend(_
, _
, LpInterior(..)) => {
1158 self.append_loan_path_to_string(loan_path
, out
)
1163 pub fn loan_path_to_string(&self, loan_path
: &LoanPath
<'tcx
>) -> String
{
1164 let mut result
= String
::new();
1165 self.append_loan_path_to_string(loan_path
, &mut result
);
1169 pub fn cmt_to_string(&self, cmt
: &mc
::cmt_
<'tcx
>) -> String
{
1170 cmt
.descriptive_string(self.tcx
)
1173 pub fn cmt_to_path_or_string(&self, cmt
: &mc
::cmt
<'tcx
>) -> String
{
1174 match opt_loan_path(cmt
) {
1175 Some(lp
) => format
!("`{}`", self.loan_path_to_string(&lp
)),
1176 None
=> self.cmt_to_string(cmt
),
1181 fn statement_scope_span(tcx
: &TyCtxt
, region
: ty
::Region
) -> Option
<Span
> {
1183 ty
::ReScope(scope
) => {
1184 match tcx
.map
.find(scope
.node_id(&tcx
.region_maps
)) {
1185 Some(hir_map
::NodeStmt(stmt
)) => Some(stmt
.span
),
1193 impl BitwiseOperator
for LoanDataFlowOperator
{
1195 fn join(&self, succ
: usize, pred
: usize) -> usize {
1196 succ
| pred
// loans from both preds are in scope
1200 impl DataFlowOperator
for LoanDataFlowOperator
{
1202 fn initial_value(&self) -> bool
{
1203 false // no loans in scope by default
1207 impl<'tcx
> fmt
::Debug
for InteriorKind
{
1208 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1210 InteriorField(mc
::NamedField(fld
)) => write
!(f
, "{}", fld
),
1211 InteriorField(mc
::PositionalField(i
)) => write
!(f
, "#{}", i
),
1212 InteriorElement(..) => write
!(f
, "[]"),
1217 impl<'tcx
> fmt
::Debug
for Loan
<'tcx
> {
1218 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1219 write
!(f
, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})",
1225 self.restricted_paths
)
1229 impl<'tcx
> fmt
::Debug
for LoanPath
<'tcx
> {
1230 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1233 write
!(f
, "$({})", ty
::tls
::with(|tcx
| tcx
.map
.node_to_string(id
)))
1236 LpUpvar(ty
::UpvarId{ var_id, closure_expr_id }
) => {
1237 let s
= ty
::tls
::with(|tcx
| tcx
.map
.node_to_string(var_id
));
1238 write
!(f
, "$({} captured by id={})", s
, closure_expr_id
)
1241 LpDowncast(ref lp
, variant_def_id
) => {
1242 let variant_str
= if variant_def_id
.is_local() {
1243 ty
::tls
::with(|tcx
| tcx
.item_path_str(variant_def_id
))
1245 format
!("{:?}", variant_def_id
)
1247 write
!(f
, "({:?}{}{})", lp
, DOWNCAST_PRINTED_OPERATOR
, variant_str
)
1250 LpExtend(ref lp
, _
, LpDeref(_
)) => {
1251 write
!(f
, "{:?}.*", lp
)
1254 LpExtend(ref lp
, _
, LpInterior(_
, ref interior
)) => {
1255 write
!(f
, "{:?}.{:?}", lp
, interior
)
1261 impl<'tcx
> fmt
::Display
for LoanPath
<'tcx
> {
1262 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1265 write
!(f
, "$({})", ty
::tls
::with(|tcx
| tcx
.map
.node_to_user_string(id
)))
1268 LpUpvar(ty
::UpvarId{ var_id, closure_expr_id: _ }
) => {
1269 let s
= ty
::tls
::with(|tcx
| tcx
.map
.node_to_user_string(var_id
));
1270 write
!(f
, "$({} captured by closure)", s
)
1273 LpDowncast(ref lp
, variant_def_id
) => {
1274 let variant_str
= if variant_def_id
.is_local() {
1275 ty
::tls
::with(|tcx
| tcx
.item_path_str(variant_def_id
))
1277 format
!("{:?}", variant_def_id
)
1279 write
!(f
, "({}{}{})", lp
, DOWNCAST_PRINTED_OPERATOR
, variant_str
)
1282 LpExtend(ref lp
, _
, LpDeref(_
)) => {
1283 write
!(f
, "{}.*", lp
)
1286 LpExtend(ref lp
, _
, LpInterior(_
, ref interior
)) => {
1287 write
!(f
, "{}.{:?}", lp
, interior
)