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
::hir
::map
as hir_map
;
24 use rustc
::hir
::map
::blocks
::FnLikeNode
;
26 use rustc
::middle
::dataflow
::DataFlowContext
;
27 use rustc
::middle
::dataflow
::BitwiseOperator
;
28 use rustc
::middle
::dataflow
::DataFlowOperator
;
29 use rustc
::middle
::dataflow
::KillFrom
;
30 use rustc
::hir
::def_id
::{DefId, DefIndex}
;
31 use rustc
::middle
::expr_use_visitor
as euv
;
32 use rustc
::middle
::mem_categorization
as mc
;
33 use rustc
::middle
::mem_categorization
::Categorization
;
34 use rustc
::middle
::mem_categorization
::ImmutabilityBlame
;
35 use rustc
::middle
::region
::{self, RegionMaps}
;
36 use rustc
::middle
::free_region
::RegionRelations
;
37 use rustc
::ty
::{self, TyCtxt}
;
38 use rustc
::ty
::maps
::Providers
;
40 use rustc_mir
::util
::borrowck_errors
::{BorrowckErrors, Origin}
;
44 use std
::hash
::{Hash, Hasher}
;
46 use syntax_pos
::{MultiSpan, Span}
;
47 use errors
::DiagnosticBuilder
;
50 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 for body_owner_def_id
in tcx
.body_owners() {
65 tcx
.borrowck(body_owner_def_id
);
69 pub fn provide(providers
: &mut Providers
) {
70 *providers
= Providers
{
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
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>, owner_def_id
: DefId
) {
84 debug
!("borrowck(body_owner_def_id={:?})", owner_def_id
);
86 let owner_id
= tcx
.hir
.as_local_node_id(owner_def_id
).unwrap();
88 match tcx
.hir
.get(owner_id
) {
89 hir_map
::NodeStructCtor(_
) |
90 hir_map
::NodeVariant(_
) => {
91 // We get invoked with anything that has MIR, but some of
92 // those things (notably the synthesized constructors from
93 // tuple structs/variants) do not have an associated body
94 // and do not need borrowchecking.
100 let body_id
= tcx
.hir
.body_owned_by(owner_id
);
101 let tables
= tcx
.typeck_tables_of(owner_def_id
);
102 let region_maps
= tcx
.region_maps(owner_def_id
);
103 let bccx
= &mut BorrowckCtxt { tcx, tables, region_maps, owner_def_id }
;
105 let body
= bccx
.tcx
.hir
.body(body_id
);
107 // Eventually, borrowck will always read the MIR, but at the
108 // moment we do not. So, for now, we always force MIR to be
109 // constructed for a given fn, since this may result in errors
110 // being reported and we want that to happen.
112 // Note that `mir_validated` is a "stealable" result; the
113 // thief, `optimized_mir()`, forces borrowck, so we know that
114 // is not yet stolen.
115 tcx
.mir_validated(owner_def_id
).borrow();
117 // option dance because you can't capture an uninitialized variable
120 if let Some(AnalysisData
{ all_loans
,
122 move_data
: flowed_moves
}) =
123 build_borrowck_dataflow_data(bccx
, false, body_id
,
125 cfg
= Some(cfg
::CFG
::new(bccx
.tcx
, &body
));
126 cfg
.as_mut().unwrap()
129 check_loans
::check_loans(bccx
, &loan_dfcx
, &flowed_moves
, &all_loans
, body
);
133 fn build_borrowck_dataflow_data
<'a
, 'c
, 'tcx
, F
>(this
: &mut BorrowckCtxt
<'a
, 'tcx
>,
134 force_analysis
: bool
,
135 body_id
: hir
::BodyId
,
137 -> Option
<AnalysisData
<'a
, 'tcx
>>
138 where F
: FnOnce(&mut BorrowckCtxt
<'a
, 'tcx
>) -> &'c cfg
::CFG
140 // Check the body of fn items.
142 let body
= tcx
.hir
.body(body_id
);
144 let mut visitor
= intravisit
::IdRangeComputingVisitor
::new(&tcx
.hir
);
145 visitor
.visit_body(body
);
148 let (all_loans
, move_data
) =
149 gather_loans
::gather_loans_in_fn(this
, body_id
);
151 if !force_analysis
&& move_data
.is_empty() && all_loans
.is_empty() {
152 // large arrays of data inserted as constants can take a lot of
153 // time and memory to borrow-check - see issue #36799. However,
154 // they don't have lvalues, so no borrow-check is actually needed.
155 // Recognize that case and skip borrow-checking.
156 debug
!("skipping loan propagation for {:?} because of no loans", body_id
);
159 debug
!("propagating loans in {:?}", body_id
);
162 let cfg
= get_cfg(this
);
164 DataFlowContext
::new(this
.tcx
,
168 LoanDataFlowOperator
,
171 for (loan_idx
, loan
) in all_loans
.iter().enumerate() {
172 loan_dfcx
.add_gen(loan
.gen_scope
.node_id(), loan_idx
);
173 loan_dfcx
.add_kill(KillFrom
::ScopeEnd
,
174 loan
.kill_scope
.node_id(), loan_idx
);
176 loan_dfcx
.add_kills_from_flow_exits(cfg
);
177 loan_dfcx
.propagate(cfg
, body
);
179 let flowed_moves
= move_data
::FlowedMoveData
::new(move_data
,
185 Some(AnalysisData
{ all_loans
,
187 move_data
:flowed_moves
})
190 /// Accessor for introspective clients inspecting `AnalysisData` and
191 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
192 pub fn build_borrowck_dataflow_data_for_fn
<'a
, 'tcx
>(
193 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
194 body_id
: hir
::BodyId
,
196 -> (BorrowckCtxt
<'a
, 'tcx
>, AnalysisData
<'a
, 'tcx
>)
198 let owner_id
= tcx
.hir
.body_owner(body_id
);
199 let owner_def_id
= tcx
.hir
.local_def_id(owner_id
);
200 let tables
= tcx
.typeck_tables_of(owner_def_id
);
201 let region_maps
= tcx
.region_maps(owner_def_id
);
202 let mut bccx
= BorrowckCtxt { tcx, tables, region_maps, owner_def_id }
;
204 let dataflow_data
= build_borrowck_dataflow_data(&mut bccx
, true, body_id
, |_
| cfg
);
205 (bccx
, dataflow_data
.unwrap())
208 // ----------------------------------------------------------------------
211 pub struct BorrowckCtxt
<'a
, 'tcx
: 'a
> {
212 tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>,
214 // tables for the current thing we are checking; set to
215 // Some in `borrowck_fn` and cleared later
216 tables
: &'a ty
::TypeckTables
<'tcx
>,
218 region_maps
: Rc
<RegionMaps
>,
223 impl<'b
, 'tcx
: 'b
> BorrowckErrors
for BorrowckCtxt
<'b
, 'tcx
> {
224 fn struct_span_err_with_code
<'a
, S
: Into
<MultiSpan
>>(&'a
self,
228 -> DiagnosticBuilder
<'a
>
230 self.tcx
.sess
.struct_span_err_with_code(sp
, msg
, code
)
233 fn struct_span_err
<'a
, S
: Into
<MultiSpan
>>(&'a
self,
236 -> DiagnosticBuilder
<'a
>
238 self.tcx
.sess
.struct_span_err(sp
, msg
)
242 ///////////////////////////////////////////////////////////////////////////
243 // Loans and loan paths
245 /// Record of a loan that was issued.
246 pub struct Loan
<'tcx
> {
248 loan_path
: Rc
<LoanPath
<'tcx
>>,
249 kind
: ty
::BorrowKind
,
250 restricted_paths
: Vec
<Rc
<LoanPath
<'tcx
>>>,
252 /// gen_scope indicates where loan is introduced. Typically the
253 /// loan is introduced at the point of the borrow, but in some
254 /// cases, notably method arguments, the loan may be introduced
255 /// only later, once it comes into scope. See also
256 /// `GatherLoanCtxt::compute_gen_scope`.
257 gen_scope
: region
::CodeExtent
,
259 /// kill_scope indicates when the loan goes out of scope. This is
260 /// either when the lifetime expires or when the local variable
261 /// which roots the loan-path goes out of scope, whichever happens
262 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
263 kill_scope
: region
::CodeExtent
,
265 cause
: euv
::LoanCause
,
268 impl<'tcx
> Loan
<'tcx
> {
269 pub fn loan_path(&self) -> Rc
<LoanPath
<'tcx
>> {
270 self.loan_path
.clone()
275 pub struct LoanPath
<'tcx
> {
276 kind
: LoanPathKind
<'tcx
>,
280 impl<'tcx
> PartialEq
for LoanPath
<'tcx
> {
281 fn eq(&self, that
: &LoanPath
<'tcx
>) -> bool
{
282 self.kind
== that
.kind
286 impl<'tcx
> Hash
for LoanPath
<'tcx
> {
287 fn hash
<H
: Hasher
>(&self, state
: &mut H
) {
288 self.kind
.hash(state
);
292 #[derive(PartialEq, Eq, Hash, Debug)]
293 pub enum LoanPathKind
<'tcx
> {
294 LpVar(ast
::NodeId
), // `x` in README.md
295 LpUpvar(ty
::UpvarId
), // `x` captured by-value into closure
296 LpDowncast(Rc
<LoanPath
<'tcx
>>, DefId
), // `x` downcast to particular enum variant
297 LpExtend(Rc
<LoanPath
<'tcx
>>, mc
::MutabilityCategory
, LoanPathElem
<'tcx
>)
300 impl<'tcx
> LoanPath
<'tcx
> {
301 fn new(kind
: LoanPathKind
<'tcx
>, ty
: ty
::Ty
<'tcx
>) -> LoanPath
<'tcx
> {
302 LoanPath { kind: kind, ty: ty }
305 fn to_type(&self) -> ty
::Ty
<'tcx
> { self.ty }
308 // FIXME (pnkfelix): See discussion here
309 // https://github.com/pnkfelix/rust/commit/
310 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
311 const DOWNCAST_PRINTED_OPERATOR
: &'
static str = " as ";
313 // A local, "cleaned" version of `mc::InteriorKind` that drops
314 // information that is not relevant to loan-path analysis. (In
315 // particular, the distinction between how precisely an array-element
316 // is tracked is irrelevant here.)
317 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
318 pub enum InteriorKind
{
319 InteriorField(mc
::FieldName
),
323 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
324 impl ToInteriorKind
for mc
::InteriorKind
{
325 fn cleaned(self) -> InteriorKind
{
327 mc
::InteriorField(name
) => InteriorField(name
),
328 mc
::InteriorElement(_
) => InteriorElement
,
334 // - a pointer dereference (`*LV` in README.md)
335 // - a field reference, with an optional definition of the containing
336 // enum variant (`LV.f` in README.md)
337 // `DefId` is present when the field is part of struct that is in
338 // a variant of an enum. For instance in:
339 // `enum E { X { foo: u32 }, Y { foo: u32 }}`
340 // each `foo` is qualified by the definitition id of the variant (`X` or `Y`).
341 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
342 pub enum LoanPathElem
<'tcx
> {
343 LpDeref(mc
::PointerKind
<'tcx
>),
344 LpInterior(Option
<DefId
>, InteriorKind
),
347 fn closure_to_block(closure_id
: DefIndex
,
348 tcx
: TyCtxt
) -> ast
::NodeId
{
349 let closure_id
= tcx
.hir
.def_index_to_node_id(closure_id
);
350 match tcx
.hir
.get(closure_id
) {
351 hir_map
::NodeExpr(expr
) => match expr
.node
{
352 hir
::ExprClosure(.., body_id
, _
) => {
356 bug
!("encountered non-closure id: {}", closure_id
)
359 _
=> bug
!("encountered non-expr id: {}", closure_id
)
363 impl<'a
, 'tcx
> LoanPath
<'tcx
> {
364 pub fn kill_scope(&self, bccx
: &BorrowckCtxt
<'a
, 'tcx
>) -> region
::CodeExtent
{
366 LpVar(local_id
) => bccx
.region_maps
.var_scope(local_id
),
367 LpUpvar(upvar_id
) => {
368 let block_id
= closure_to_block(upvar_id
.closure_expr_id
, bccx
.tcx
);
369 region
::CodeExtent
::Misc(block_id
)
371 LpDowncast(ref base
, _
) |
372 LpExtend(ref base
, ..) => base
.kill_scope(bccx
),
376 fn has_fork(&self, other
: &LoanPath
<'tcx
>) -> bool
{
377 match (&self.kind
, &other
.kind
) {
378 (&LpExtend(ref base
, _
, LpInterior(opt_variant_id
, id
)),
379 &LpExtend(ref base2
, _
, LpInterior(opt_variant_id2
, id2
))) =>
380 if id
== id2
&& opt_variant_id
== opt_variant_id2
{
381 base
.has_fork(&base2
)
385 (&LpExtend(ref base
, _
, LpDeref(_
)), _
) => base
.has_fork(other
),
386 (_
, &LpExtend(ref base
, _
, LpDeref(_
))) => self.has_fork(&base
),
391 fn depth(&self) -> usize {
393 LpExtend(ref base
, _
, LpDeref(_
)) => base
.depth(),
394 LpExtend(ref base
, _
, LpInterior(..)) => base
.depth() + 1,
399 fn common(&self, other
: &LoanPath
<'tcx
>) -> Option
<LoanPath
<'tcx
>> {
400 match (&self.kind
, &other
.kind
) {
401 (&LpExtend(ref base
, a
, LpInterior(opt_variant_id
, id
)),
402 &LpExtend(ref base2
, _
, LpInterior(opt_variant_id2
, id2
))) => {
403 if id
== id2
&& opt_variant_id
== opt_variant_id2
{
404 base
.common(&base2
).map(|x
| {
406 if base
.depth() == xd
&& base2
.depth() == xd
{
408 kind
: LpExtend(Rc
::new(x
), a
, LpInterior(opt_variant_id
, id
)),
419 (&LpExtend(ref base
, _
, LpDeref(_
)), _
) => base
.common(other
),
420 (_
, &LpExtend(ref other
, _
, LpDeref(_
))) => self.common(&other
),
421 (&LpVar(id
), &LpVar(id2
)) => {
423 Some(LoanPath { kind: LpVar(id), ty: self.ty }
)
428 (&LpUpvar(id
), &LpUpvar(id2
)) => {
430 Some(LoanPath { kind: LpUpvar(id), ty: self.ty }
)
440 pub fn opt_loan_path
<'tcx
>(cmt
: &mc
::cmt
<'tcx
>) -> Option
<Rc
<LoanPath
<'tcx
>>> {
441 //! Computes the `LoanPath` (if any) for a `cmt`.
442 //! Note that this logic is somewhat duplicated in
443 //! the method `compute()` found in `gather_loans::restrictions`,
444 //! which allows it to share common loan path pieces as it
445 //! traverses the CMT.
447 let new_lp
= |v
: LoanPathKind
<'tcx
>| Rc
::new(LoanPath
::new(v
, cmt
.ty
));
450 Categorization
::Rvalue(..) |
451 Categorization
::StaticItem
=> {
455 Categorization
::Local(id
) => {
456 Some(new_lp(LpVar(id
)))
459 Categorization
::Upvar(mc
::Upvar { id, .. }
) => {
460 Some(new_lp(LpUpvar(id
)))
463 Categorization
::Deref(ref cmt_base
, pk
) => {
464 opt_loan_path(cmt_base
).map(|lp
| {
465 new_lp(LpExtend(lp
, cmt
.mutbl
, LpDeref(pk
)))
469 Categorization
::Interior(ref cmt_base
, ik
) => {
470 opt_loan_path(cmt_base
).map(|lp
| {
471 let opt_variant_id
= match cmt_base
.cat
{
472 Categorization
::Downcast(_
, did
) => Some(did
),
475 new_lp(LpExtend(lp
, cmt
.mutbl
, LpInterior(opt_variant_id
, ik
.cleaned())))
479 Categorization
::Downcast(ref cmt_base
, variant_def_id
) =>
480 opt_loan_path(cmt_base
)
482 new_lp(LpDowncast(lp
, variant_def_id
))
488 ///////////////////////////////////////////////////////////////////////////
491 // Errors that can occur
492 #[derive(Debug, PartialEq)]
493 pub enum bckerr_code
<'tcx
> {
495 /// superscope, subscope, loan cause
496 err_out_of_scope(ty
::Region
<'tcx
>, ty
::Region
<'tcx
>, euv
::LoanCause
),
497 err_borrowed_pointer_too_short(ty
::Region
<'tcx
>, ty
::Region
<'tcx
>), // loan, ptr
500 // Combination of an error code and the categorization of the expression
502 #[derive(Debug, PartialEq)]
503 pub struct BckError
<'tcx
> {
505 cause
: AliasableViolationKind
,
507 code
: bckerr_code
<'tcx
>
510 #[derive(Copy, Clone, Debug, PartialEq)]
511 pub enum AliasableViolationKind
{
513 BorrowViolation(euv
::LoanCause
)
516 #[derive(Copy, Clone, Debug)]
517 pub enum MovedValueUseKind
{
522 ///////////////////////////////////////////////////////////////////////////
525 impl<'a
, 'tcx
> BorrowckCtxt
<'a
, 'tcx
> {
526 pub fn is_subregion_of(&self,
527 r_sub
: ty
::Region
<'tcx
>,
528 r_sup
: ty
::Region
<'tcx
>)
531 let region_rels
= RegionRelations
::new(self.tcx
,
534 &self.tables
.free_region_map
);
535 region_rels
.is_subregion_of(r_sub
, r_sup
)
538 pub fn report(&self, err
: BckError
<'tcx
>) {
539 // Catch and handle some particular cases.
540 match (&err
.code
, &err
.cause
) {
541 (&err_out_of_scope(&ty
::ReScope(_
), &ty
::ReStatic
, _
),
542 &BorrowViolation(euv
::ClosureCapture(span
))) |
543 (&err_out_of_scope(&ty
::ReScope(_
), &ty
::ReEarlyBound(..), _
),
544 &BorrowViolation(euv
::ClosureCapture(span
))) |
545 (&err_out_of_scope(&ty
::ReScope(_
), &ty
::ReFree(..), _
),
546 &BorrowViolation(euv
::ClosureCapture(span
))) => {
547 return self.report_out_of_scope_escaping_closure_capture(&err
, span
);
552 let mut db
= self.bckerr_to_diag(&err
);
553 self.note_and_explain_bckerr(&mut db
, err
);
557 pub fn report_use_of_moved_value(&self,
559 use_kind
: MovedValueUseKind
,
561 the_move
: &move_data
::Move
,
562 moved_lp
: &LoanPath
<'tcx
>,
563 _param_env
: ty
::ParamEnv
<'tcx
>) {
564 let (verb
, verb_participle
) = match use_kind
{
565 MovedInUse
=> ("use", "used"),
566 MovedInCapture
=> ("capture", "captured"),
569 let (_ol
, _moved_lp_msg
, mut err
, need_note
) = match the_move
.kind
{
570 move_data
::Declared
=> {
571 // If this is an uninitialized variable, just emit a simple warning
573 self.cannot_act_on_uninitialized_variable(use_span
,
575 &self.loan_path_to_string(lp
),
577 .span_label(use_span
, format
!("use of possibly uninitialized `{}`",
578 self.loan_path_to_string(lp
)))
583 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
584 // normally generate a rather confusing message:
586 // error: use of moved value: `x.b`
587 // note: `x.a` moved here...
589 // What we want to do instead is get the 'common ancestor' of the two moves and
590 // use that for most of the message instead, giving is something like this:
592 // error: use of moved value: `x`
593 // note: `x` moved here (through moving `x.a`)...
595 let common
= moved_lp
.common(lp
);
596 let has_common
= common
.is_some();
597 let has_fork
= moved_lp
.has_fork(lp
);
598 let (nl
, ol
, moved_lp_msg
) =
599 if has_fork
&& has_common
{
600 let nl
= self.loan_path_to_string(&common
.unwrap());
602 let moved_lp_msg
= format
!(" (through moving `{}`)",
603 self.loan_path_to_string(moved_lp
));
604 (nl
, ol
, moved_lp_msg
)
606 (self.loan_path_to_string(lp
),
607 self.loan_path_to_string(moved_lp
),
611 let partial
= moved_lp
.depth() > lp
.depth();
612 let msg
= if !has_fork
&& partial { "partially " }
613 else if has_fork
&& !has_common { "collaterally "}
615 let mut err
= struct_span_err
!(
616 self.tcx
.sess
, use_span
, E0382
,
617 "{} of {}moved value: `{}`",
619 let need_note
= match lp
.ty
.sty
{
620 ty
::TypeVariants
::TyClosure(id
, _
) => {
621 let node_id
= self.tcx
.hir
.as_local_node_id(id
).unwrap();
622 let hir_id
= self.tcx
.hir
.node_to_hir_id(node_id
);
623 if let Some(&(ty
::ClosureKind
::FnOnce
, Some((span
, name
)))) =
624 self.tables
.closure_kinds().get(hir_id
)
626 err
.span_note(span
, &format
!(
627 "closure cannot be invoked more than once because \
628 it moves the variable `{}` out of its environment",
638 (ol
, moved_lp_msg
, err
, need_note
)
642 // Get type of value and span where it was previously
644 let (move_span
, move_note
) = match the_move
.kind
{
645 move_data
::Declared
=> {
649 move_data
::MoveExpr
|
650 move_data
::MovePat
=>
651 (self.tcx
.hir
.span(the_move
.id
), ""),
653 move_data
::Captured
=>
654 (match self.tcx
.hir
.expect_expr(the_move
.id
).node
{
655 hir
::ExprClosure(.., fn_decl_span
) => fn_decl_span
,
656 ref r
=> bug
!("Captured({}) maps to non-closure: {:?}",
658 }, " (into closure)"),
661 // Annotate the use and the move in the span. Watch out for
662 // the case where the use and the move are the same. This
663 // means the use is in a loop.
664 err
= if use_span
== move_span
{
667 format
!("value moved{} here in previous iteration of loop",
671 err
.span_label(use_span
, format
!("value {} here after move", verb_participle
))
672 .span_label(move_span
, format
!("value moved{} here", move_note
));
677 err
.note(&format
!("move occurs because `{}` has type `{}`, \
678 which does not implement the `Copy` trait",
679 self.loan_path_to_string(moved_lp
),
683 // Note: we used to suggest adding a `ref binding` or calling
684 // `clone` but those suggestions have been removed because
685 // they are often not what you actually want to do, and were
686 // not considered particularly helpful.
691 pub fn report_partial_reinitialization_of_uninitialized_structure(
694 lp
: &LoanPath
<'tcx
>) {
696 self.tcx
.sess
, span
, E0383
,
697 "partial reinitialization of uninitialized structure `{}`",
698 self.loan_path_to_string(lp
));
701 pub fn report_reassigned_immutable_variable(&self,
705 &move_data
::Assignment
) {
706 let mut err
= self.cannot_reassign_immutable(span
,
707 &self.loan_path_to_string(lp
),
709 err
.span_label(span
, "re-assignment of immutable variable");
710 if span
!= assign
.span
{
711 err
.span_label(assign
.span
, format
!("first assignment to `{}`",
712 self.loan_path_to_string(lp
)));
717 pub fn struct_span_err_with_code
<S
: Into
<MultiSpan
>>(&self,
721 -> DiagnosticBuilder
<'a
> {
722 self.tcx
.sess
.struct_span_err_with_code(s
, msg
, code
)
725 fn bckerr_to_diag(&self, err
: &BckError
<'tcx
>) -> DiagnosticBuilder
<'a
> {
726 let span
= err
.span
.clone();
730 let descr
= match err
.cmt
.note
{
731 mc
::NoteClosureEnv(_
) | mc
::NoteUpvarRef(_
) => {
732 self.cmt_to_string(&err
.cmt
)
734 _
=> match opt_loan_path(&err
.cmt
) {
737 err
.cmt
.mutbl
.to_user_str(),
738 self.cmt_to_string(&err
.cmt
))
742 format
!("{} {} `{}`",
743 err
.cmt
.mutbl
.to_user_str(),
744 self.cmt_to_string(&err
.cmt
),
745 self.loan_path_to_string(&lp
))
751 MutabilityViolation
=> {
752 struct_span_err
!(self.tcx
.sess
, span
, E0594
, "cannot assign to {}", descr
)
754 BorrowViolation(euv
::ClosureCapture(_
)) => {
755 struct_span_err
!(self.tcx
.sess
, span
, E0595
,
756 "closure cannot assign to {}", descr
)
758 BorrowViolation(euv
::OverloadedOperator
) |
759 BorrowViolation(euv
::AddrOf
) |
760 BorrowViolation(euv
::RefBinding
) |
761 BorrowViolation(euv
::AutoRef
) |
762 BorrowViolation(euv
::AutoUnsafe
) |
763 BorrowViolation(euv
::ForLoop
) |
764 BorrowViolation(euv
::MatchDiscriminant
) => {
765 struct_span_err
!(self.tcx
.sess
, span
, E0596
,
766 "cannot borrow {} as mutable", descr
)
768 BorrowViolation(euv
::ClosureInvocation
) => {
770 "err_mutbl with a closure invocation");
774 err_out_of_scope(..) => {
775 let msg
= match opt_loan_path(&err
.cmt
) {
776 None
=> "borrowed value".to_string(),
778 format
!("`{}`", self.loan_path_to_string(&lp
))
781 struct_span_err
!(self.tcx
.sess
, span
, E0597
, "{} does not live long enough", msg
)
783 err_borrowed_pointer_too_short(..) => {
784 let descr
= self.cmt_to_path_or_string(&err
.cmt
);
785 struct_span_err
!(self.tcx
.sess
, span
, E0598
,
786 "lifetime of {} is too short to guarantee \
787 its contents can be safely reborrowed",
793 pub fn report_aliasability_violation(&self,
795 kind
: AliasableViolationKind
,
796 cause
: mc
::AliasableReason
,
797 cmt
: mc
::cmt
<'tcx
>) {
798 let mut is_closure
= false;
799 let prefix
= match kind
{
800 MutabilityViolation
=> {
801 "cannot assign to data"
803 BorrowViolation(euv
::ClosureCapture(_
)) |
804 BorrowViolation(euv
::OverloadedOperator
) |
805 BorrowViolation(euv
::AddrOf
) |
806 BorrowViolation(euv
::AutoRef
) |
807 BorrowViolation(euv
::AutoUnsafe
) |
808 BorrowViolation(euv
::RefBinding
) |
809 BorrowViolation(euv
::MatchDiscriminant
) => {
810 "cannot borrow data mutably"
813 BorrowViolation(euv
::ClosureInvocation
) => {
818 BorrowViolation(euv
::ForLoop
) => {
824 mc
::AliasableStatic
|
825 mc
::AliasableStaticMut
=> {
826 // This path cannot occur. It happens when we have an
827 // `&mut` or assignment to a static. But in the case
828 // of `static X`, we get a mutability violation first,
829 // and never get here. In the case of `static mut X`,
830 // that is unsafe and hence the aliasability error is
832 span_bug
!(span
, "aliasability violation for static `{}`", prefix
)
834 mc
::AliasableBorrowed
=> {}
836 let blame
= cmt
.immutability_blame();
837 let mut err
= match blame
{
838 Some(ImmutabilityBlame
::ClosureEnv(id
)) => {
839 let mut err
= struct_span_err
!(
840 self.tcx
.sess
, span
, E0387
,
841 "{} in a captured outer variable in an `Fn` closure", prefix
);
843 // FIXME: the distinction between these 2 messages looks wrong.
844 let help
= if let BorrowViolation(euv
::ClosureCapture(_
)) = kind
{
845 // The aliasability violation with closure captures can
846 // happen for nested closures, so we know the enclosing
847 // closure incorrectly accepts an `Fn` while it needs to
849 "consider changing this to accept closures that implement `FnMut`"
852 "consider changing this closure to take self by mutable reference"
854 let node_id
= self.tcx
.hir
.def_index_to_node_id(id
);
855 err
.span_help(self.tcx
.hir
.span(node_id
), help
);
859 let mut err
= struct_span_err
!(
860 self.tcx
.sess
, span
, E0389
,
861 "{} in a `&` reference", prefix
);
862 err
.span_label(span
, "assignment into an immutable reference");
866 self.note_immutability_blame(&mut err
, blame
);
869 err
.help("closures behind references must be called via `&mut`");
874 /// Given a type, if it is an immutable reference, return a suggestion to make it mutable
875 fn suggest_mut_for_immutable(&self, pty
: &hir
::Ty
, is_implicit_self
: bool
) -> Option
<String
> {
876 // Check wether the argument is an immutable reference
877 debug
!("suggest_mut_for_immutable({:?}, {:?})", pty
, is_implicit_self
);
878 if let hir
::TyRptr(lifetime
, hir
::MutTy
{
879 mutbl
: hir
::Mutability
::MutImmutable
,
882 // Account for existing lifetimes when generating the message
883 let pointee_snippet
= match self.tcx
.sess
.codemap().span_to_snippet(ty
.span
) {
884 Ok(snippet
) => snippet
,
888 let lifetime_snippet
= if !lifetime
.is_elided() {
889 format
!("{} ", match self.tcx
.sess
.codemap().span_to_snippet(lifetime
.span
) {
890 Ok(lifetime_snippet
) => lifetime_snippet
,
896 Some(format
!("use `&{}mut {}` here to make mutable",
898 if is_implicit_self { "self" }
else { &*pointee_snippet }
))
904 fn local_binding_mode(&self, node_id
: ast
::NodeId
) -> ty
::BindingMode
{
905 let pat
= match self.tcx
.hir
.get(node_id
) {
906 hir_map
::Node
::NodeBinding(pat
) => pat
,
907 node
=> bug
!("bad node for local: {:?}", node
)
911 hir
::PatKind
::Binding(..) => {
915 .expect("missing binding mode")
917 _
=> bug
!("local is not a binding: {:?}", pat
)
921 fn local_ty(&self, node_id
: ast
::NodeId
) -> (Option
<&hir
::Ty
>, bool
) {
922 let parent
= self.tcx
.hir
.get_parent_node(node_id
);
923 let parent_node
= self.tcx
.hir
.get(parent
);
925 // The parent node is like a fn
926 if let Some(fn_like
) = FnLikeNode
::from_node(parent_node
) {
927 // `nid`'s parent's `Body`
928 let fn_body
= self.tcx
.hir
.body(fn_like
.body());
929 // Get the position of `node_id` in the arguments list
930 let arg_pos
= fn_body
.arguments
.iter().position(|arg
| arg
.pat
.id
== node_id
);
931 if let Some(i
) = arg_pos
{
932 // The argument's `Ty`
933 (Some(&fn_like
.decl().inputs
[i
]),
934 i
== 0 && fn_like
.decl().has_implicit_self
)
943 fn note_immutability_blame(&self,
944 db
: &mut DiagnosticBuilder
,
945 blame
: Option
<ImmutabilityBlame
>) {
948 Some(ImmutabilityBlame
::ClosureEnv(_
)) => {}
949 Some(ImmutabilityBlame
::ImmLocal(node_id
)) => {
950 let let_span
= self.tcx
.hir
.span(node_id
);
951 if let ty
::BindByValue(..) = self.local_binding_mode(node_id
) {
952 if let Ok(snippet
) = self.tcx
.sess
.codemap().span_to_snippet(let_span
) {
953 let (_
, is_implicit_self
) = self.local_ty(node_id
);
954 if is_implicit_self
&& snippet
!= "self" {
955 // avoid suggesting `mut &self`.
960 format
!("consider changing this to `mut {}`", snippet
)
965 Some(ImmutabilityBlame
::LocalDeref(node_id
)) => {
966 let let_span
= self.tcx
.hir
.span(node_id
);
967 match self.local_binding_mode(node_id
) {
968 ty
::BindByReference(..) => {
969 let snippet
= self.tcx
.sess
.codemap().span_to_snippet(let_span
);
970 if let Ok(snippet
) = snippet
{
973 format
!("consider changing this to `{}`",
974 snippet
.replace("ref ", "ref mut "))
978 ty
::BindByValue(..) => {
979 if let (Some(local_ty
), is_implicit_self
) = self.local_ty(node_id
) {
981 self.suggest_mut_for_immutable(local_ty
, is_implicit_self
) {
982 db
.span_label(local_ty
.span
, msg
);
988 Some(ImmutabilityBlame
::AdtFieldDeref(_
, field
)) => {
989 let node_id
= match self.tcx
.hir
.as_local_node_id(field
.did
) {
990 Some(node_id
) => node_id
,
994 if let hir_map
::Node
::NodeField(ref field
) = self.tcx
.hir
.get(node_id
) {
995 if let Some(msg
) = self.suggest_mut_for_immutable(&field
.ty
, false) {
996 db
.span_label(field
.ty
.span
, msg
);
1003 fn report_out_of_scope_escaping_closure_capture(&self,
1004 err
: &BckError
<'tcx
>,
1007 let cmt_path_or_string
= self.cmt_to_path_or_string(&err
.cmt
);
1010 match self.tcx
.sess
.codemap().span_to_snippet(err
.span
) {
1011 Ok(string
) => format
!("move {}", string
),
1012 Err(_
) => format
!("move |<args>| <body>")
1015 struct_span_err
!(self.tcx
.sess
, err
.span
, E0373
,
1016 "closure may outlive the current function, \
1017 but it borrows {}, \
1018 which is owned by the current function",
1020 .span_label(capture_span
,
1021 format
!("{} is borrowed here",
1022 cmt_path_or_string
))
1023 .span_label(err
.span
,
1024 format
!("may outlive borrowed value {}",
1025 cmt_path_or_string
))
1026 .span_suggestion(err
.span
,
1027 &format
!("to force the closure to take ownership of {} \
1028 (and any other referenced variables), \
1029 use the `move` keyword",
1030 cmt_path_or_string
),
1035 fn region_end_span(&self, region
: ty
::Region
<'tcx
>) -> Option
<Span
> {
1037 ty
::ReScope(scope
) => {
1038 match scope
.span(&self.tcx
.hir
) {
1051 fn note_and_explain_bckerr(&self, db
: &mut DiagnosticBuilder
, err
: BckError
<'tcx
>) {
1052 let error_span
= err
.span
.clone();
1055 self.note_and_explain_mutbl_error(db
, &err
, &error_span
);
1056 self.note_immutability_blame(db
, err
.cmt
.immutability_blame());
1058 err_out_of_scope(super_scope
, sub_scope
, cause
) => {
1059 let (value_kind
, value_msg
) = match err
.cmt
.cat
{
1060 mc
::Categorization
::Rvalue(..) =>
1061 ("temporary value", "temporary value created here"),
1063 ("borrowed value", "borrow occurs here")
1066 let is_closure
= match cause
{
1067 euv
::ClosureCapture(s
) => {
1068 // The primary span starts out as the closure creation point.
1069 // Change the primary span here to highlight the use of the variable
1070 // in the closure, because it seems more natural. Highlight
1071 // closure creation point as a secondary span.
1072 match db
.span
.primary_span() {
1074 db
.span
= MultiSpan
::from_span(s
);
1075 db
.span_label(primary
, "capture occurs here");
1076 db
.span_label(s
, "does not live long enough");
1083 db
.span_label(error_span
, "does not live long enough");
1088 let sub_span
= self.region_end_span(sub_scope
);
1089 let super_span
= self.region_end_span(super_scope
);
1091 match (sub_span
, super_span
) {
1092 (Some(s1
), Some(s2
)) if s1
== s2
=> {
1094 db
.span
= MultiSpan
::from_span(s1
);
1095 db
.span_label(error_span
, value_msg
);
1096 let msg
= match opt_loan_path(&err
.cmt
) {
1097 None
=> value_kind
.to_string(),
1099 format
!("`{}`", self.loan_path_to_string(&lp
))
1103 format
!("{} dropped here while still borrowed", msg
));
1105 db
.span_label(s1
, format
!("{} dropped before borrower", value_kind
));
1107 db
.note("values in a scope are dropped in the opposite order \
1110 (Some(s1
), Some(s2
)) if !is_closure
=> {
1111 db
.span
= MultiSpan
::from_span(s2
);
1112 db
.span_label(error_span
, value_msg
);
1113 let msg
= match opt_loan_path(&err
.cmt
) {
1114 None
=> value_kind
.to_string(),
1116 format
!("`{}`", self.loan_path_to_string(&lp
))
1119 db
.span_label(s2
, format
!("{} dropped here while still borrowed", msg
));
1120 db
.span_label(s1
, format
!("{} needs to live until here", value_kind
));
1125 db
.span_label(s
, format
!("{} needs to live until here",
1129 self.tcx
.note_and_explain_region(
1131 "borrowed value must be valid for ",
1138 db
.span_label(s
, format
!("{} only lives until here", value_kind
));
1141 self.tcx
.note_and_explain_region(
1143 "...but borrowed value is only valid for ",
1151 if let Some(_
) = statement_scope_span(self.tcx
, super_scope
) {
1152 db
.note("consider using a `let` binding to increase its lifetime");
1156 err_borrowed_pointer_too_short(loan_scope
, ptr_scope
) => {
1157 let descr
= match opt_loan_path(&err
.cmt
) {
1159 format
!("`{}`", self.loan_path_to_string(&lp
))
1161 None
=> self.cmt_to_string(&err
.cmt
),
1163 self.tcx
.note_and_explain_region(
1165 &format
!("{} would have to be valid for ",
1169 self.tcx
.note_and_explain_region(
1171 &format
!("...but {} is only valid for ", descr
),
1178 fn note_and_explain_mutbl_error(&self, db
: &mut DiagnosticBuilder
, err
: &BckError
<'tcx
>,
1179 error_span
: &Span
) {
1180 match err
.cmt
.note
{
1181 mc
::NoteClosureEnv(upvar_id
) | mc
::NoteUpvarRef(upvar_id
) => {
1182 // If this is an `Fn` closure, it simply can't mutate upvars.
1183 // If it's an `FnMut` closure, the original variable was declared immutable.
1184 // We need to determine which is the case here.
1185 let kind
= match err
.cmt
.upvar().unwrap().cat
{
1186 Categorization
::Upvar(mc
::Upvar { kind, .. }
) => kind
,
1189 if kind
== ty
::ClosureKind
::Fn
{
1190 let closure_node_id
=
1191 self.tcx
.hir
.def_index_to_node_id(upvar_id
.closure_expr_id
);
1192 db
.span_help(self.tcx
.hir
.span(closure_node_id
),
1193 "consider changing this closure to take \
1194 self by mutable reference");
1198 if let Categorization
::Deref(..) = err
.cmt
.cat
{
1199 db
.span_label(*error_span
, "cannot borrow as mutable");
1200 } else if let Categorization
::Local(local_id
) = err
.cmt
.cat
{
1201 let span
= self.tcx
.hir
.span(local_id
);
1202 if let Ok(snippet
) = self.tcx
.sess
.codemap().span_to_snippet(span
) {
1203 if snippet
.starts_with("ref mut ") || snippet
.starts_with("&mut ") {
1204 db
.span_label(*error_span
, "cannot reborrow mutably");
1205 db
.span_label(*error_span
, "try removing `&mut` here");
1207 db
.span_label(*error_span
, "cannot borrow mutably");
1210 db
.span_label(*error_span
, "cannot borrow mutably");
1212 } else if let Categorization
::Interior(ref cmt
, _
) = err
.cmt
.cat
{
1213 if let mc
::MutabilityCategory
::McImmutable
= cmt
.mutbl
{
1214 db
.span_label(*error_span
,
1215 "cannot mutably borrow immutable field");
1221 pub fn append_loan_path_to_string(&self,
1222 loan_path
: &LoanPath
<'tcx
>,
1224 match loan_path
.kind
{
1225 LpUpvar(ty
::UpvarId { var_id: id, closure_expr_id: _ }
) => {
1226 out
.push_str(&self.tcx
.local_var_name_str_def_index(id
));
1229 out
.push_str(&self.tcx
.local_var_name_str(id
));
1232 LpDowncast(ref lp_base
, variant_def_id
) => {
1234 self.append_loan_path_to_string(&lp_base
, out
);
1235 out
.push_str(DOWNCAST_PRINTED_OPERATOR
);
1236 out
.push_str(&self.tcx
.item_path_str(variant_def_id
));
1240 LpExtend(ref lp_base
, _
, LpInterior(_
, InteriorField(fname
))) => {
1241 self.append_autoderefd_loan_path_to_string(&lp_base
, out
);
1243 mc
::NamedField(fname
) => {
1245 out
.push_str(&fname
.as_str());
1247 mc
::PositionalField(idx
) => {
1249 out
.push_str(&idx
.to_string());
1254 LpExtend(ref lp_base
, _
, LpInterior(_
, InteriorElement
)) => {
1255 self.append_autoderefd_loan_path_to_string(&lp_base
, out
);
1256 out
.push_str("[..]");
1259 LpExtend(ref lp_base
, _
, LpDeref(_
)) => {
1261 self.append_loan_path_to_string(&lp_base
, out
);
1266 pub fn append_autoderefd_loan_path_to_string(&self,
1267 loan_path
: &LoanPath
<'tcx
>,
1269 match loan_path
.kind
{
1270 LpExtend(ref lp_base
, _
, LpDeref(_
)) => {
1271 // For a path like `(*x).f` or `(*x)[3]`, autoderef
1272 // rules would normally allow users to omit the `*x`.
1273 // So just serialize such paths to `x.f` or x[3]` respectively.
1274 self.append_autoderefd_loan_path_to_string(&lp_base
, out
)
1277 LpDowncast(ref lp_base
, variant_def_id
) => {
1279 self.append_autoderefd_loan_path_to_string(&lp_base
, out
);
1281 out
.push_str(&self.tcx
.item_path_str(variant_def_id
));
1285 LpVar(..) | LpUpvar(..) | LpExtend(.., LpInterior(..)) => {
1286 self.append_loan_path_to_string(loan_path
, out
)
1291 pub fn loan_path_to_string(&self, loan_path
: &LoanPath
<'tcx
>) -> String
{
1292 let mut result
= String
::new();
1293 self.append_loan_path_to_string(loan_path
, &mut result
);
1297 pub fn cmt_to_string(&self, cmt
: &mc
::cmt_
<'tcx
>) -> String
{
1298 cmt
.descriptive_string(self.tcx
)
1301 pub fn cmt_to_path_or_string(&self, cmt
: &mc
::cmt
<'tcx
>) -> String
{
1302 match opt_loan_path(cmt
) {
1303 Some(lp
) => format
!("`{}`", self.loan_path_to_string(&lp
)),
1304 None
=> self.cmt_to_string(cmt
),
1309 fn statement_scope_span(tcx
: TyCtxt
, region
: ty
::Region
) -> Option
<Span
> {
1311 ty
::ReScope(scope
) => {
1312 match tcx
.hir
.find(scope
.node_id()) {
1313 Some(hir_map
::NodeStmt(stmt
)) => Some(stmt
.span
),
1321 impl BitwiseOperator
for LoanDataFlowOperator
{
1323 fn join(&self, succ
: usize, pred
: usize) -> usize {
1324 succ
| pred
// loans from both preds are in scope
1328 impl DataFlowOperator
for LoanDataFlowOperator
{
1330 fn initial_value(&self) -> bool
{
1331 false // no loans in scope by default
1335 impl<'tcx
> fmt
::Debug
for InteriorKind
{
1336 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1338 InteriorField(mc
::NamedField(fld
)) => write
!(f
, "{}", fld
),
1339 InteriorField(mc
::PositionalField(i
)) => write
!(f
, "#{}", i
),
1340 InteriorElement
=> write
!(f
, "[]"),
1345 impl<'tcx
> fmt
::Debug
for Loan
<'tcx
> {
1346 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1347 write
!(f
, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})",
1353 self.restricted_paths
)
1357 impl<'tcx
> fmt
::Debug
for LoanPath
<'tcx
> {
1358 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1361 write
!(f
, "$({})", ty
::tls
::with(|tcx
| tcx
.hir
.node_to_string(id
)))
1364 LpUpvar(ty
::UpvarId{ var_id, closure_expr_id }
) => {
1365 let s
= ty
::tls
::with(|tcx
| {
1366 let var_node_id
= tcx
.hir
.def_index_to_node_id(var_id
);
1367 tcx
.hir
.node_to_string(var_node_id
)
1369 write
!(f
, "$({} captured by id={:?})", s
, closure_expr_id
)
1372 LpDowncast(ref lp
, variant_def_id
) => {
1373 let variant_str
= if variant_def_id
.is_local() {
1374 ty
::tls
::with(|tcx
| tcx
.item_path_str(variant_def_id
))
1376 format
!("{:?}", variant_def_id
)
1378 write
!(f
, "({:?}{}{})", lp
, DOWNCAST_PRINTED_OPERATOR
, variant_str
)
1381 LpExtend(ref lp
, _
, LpDeref(_
)) => {
1382 write
!(f
, "{:?}.*", lp
)
1385 LpExtend(ref lp
, _
, LpInterior(_
, ref interior
)) => {
1386 write
!(f
, "{:?}.{:?}", lp
, interior
)
1392 impl<'tcx
> fmt
::Display
for LoanPath
<'tcx
> {
1393 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1396 write
!(f
, "$({})", ty
::tls
::with(|tcx
| tcx
.hir
.node_to_user_string(id
)))
1399 LpUpvar(ty
::UpvarId{ var_id, closure_expr_id: _ }
) => {
1400 let s
= ty
::tls
::with(|tcx
| {
1401 let var_node_id
= tcx
.hir
.def_index_to_node_id(var_id
);
1402 tcx
.hir
.node_to_string(var_node_id
)
1404 write
!(f
, "$({} captured by closure)", s
)
1407 LpDowncast(ref lp
, variant_def_id
) => {
1408 let variant_str
= if variant_def_id
.is_local() {
1409 ty
::tls
::with(|tcx
| tcx
.item_path_str(variant_def_id
))
1411 format
!("{:?}", variant_def_id
)
1413 write
!(f
, "({}{}{})", lp
, DOWNCAST_PRINTED_OPERATOR
, variant_str
)
1416 LpExtend(ref lp
, _
, LpDeref(_
)) => {
1417 write
!(f
, "{}.*", lp
)
1420 LpExtend(ref lp
, _
, LpInterior(_
, ref interior
)) => {
1421 write
!(f
, "{}.{:?}", lp
, interior
)