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
::*;
24 use rustc
::ast_map
::blocks
::{FnLikeNode, FnParts}
;
25 use rustc
::middle
::cfg
;
26 use rustc
::middle
::dataflow
::DataFlowContext
;
27 use rustc
::middle
::dataflow
::BitwiseOperator
;
28 use rustc
::middle
::dataflow
::DataFlowOperator
;
29 use rustc
::middle
::dataflow
::KillFrom
;
30 use rustc
::middle
::expr_use_visitor
as euv
;
31 use rustc
::middle
::free_region
::FreeRegionMap
;
32 use rustc
::middle
::mem_categorization
as mc
;
33 use rustc
::middle
::region
;
34 use rustc
::middle
::ty
::{self, Ty}
;
41 use syntax
::codemap
::Span
;
43 use syntax
::visit
::{Visitor, FnKind}
;
44 use syntax
::ast
::{FnDecl, Block, NodeId}
;
52 #[derive(Clone, Copy)]
53 pub struct LoanDataFlowOperator
;
55 pub type LoanDataFlow
<'a
, 'tcx
> = DataFlowContext
<'a
, 'tcx
, LoanDataFlowOperator
>;
57 impl<'a
, 'tcx
, 'v
> Visitor
<'v
> for BorrowckCtxt
<'a
, 'tcx
> {
58 fn visit_fn(&mut self, fk
: FnKind
<'v
>, fd
: &'v FnDecl
,
59 b
: &'v Block
, s
: Span
, id
: ast
::NodeId
) {
62 visit
::FkMethod(..) => {
63 let new_free_region_map
= self.tcx
.free_region_map(id
);
64 let old_free_region_map
=
65 mem
::replace(&mut self.free_region_map
, new_free_region_map
);
66 borrowck_fn(self, fk
, fd
, b
, s
, id
);
67 self.free_region_map
= old_free_region_map
;
71 borrowck_fn(self, fk
, fd
, b
, s
, id
);
76 fn visit_item(&mut self, item
: &ast
::Item
) {
77 borrowck_item(self, item
);
80 fn visit_trait_item(&mut self, ti
: &ast
::TraitItem
) {
81 if let ast
::ConstTraitItem(_
, Some(ref expr
)) = ti
.node
{
82 gather_loans
::gather_loans_in_static_initializer(self, &*expr
);
84 visit
::walk_trait_item(self, ti
);
87 fn visit_impl_item(&mut self, ii
: &ast
::ImplItem
) {
88 if let ast
::ConstImplItem(_
, ref expr
) = ii
.node
{
89 gather_loans
::gather_loans_in_static_initializer(self, &*expr
);
91 visit
::walk_impl_item(self, ii
);
95 pub fn check_crate(tcx
: &ty
::ctxt
) {
96 let mut bccx
= BorrowckCtxt
{
98 free_region_map
: FreeRegionMap
::new(),
100 loaned_paths_same
: 0,
107 visit
::walk_crate(&mut bccx
, tcx
.map
.krate());
109 if tcx
.sess
.borrowck_stats() {
110 println
!("--- borrowck stats ---");
111 println
!("paths requiring guarantees: {}",
112 bccx
.stats
.guaranteed_paths
);
113 println
!("paths requiring loans : {}",
114 make_stat(&bccx
, bccx
.stats
.loaned_paths_same
));
115 println
!("paths requiring imm loans : {}",
116 make_stat(&bccx
, bccx
.stats
.loaned_paths_imm
));
117 println
!("stable paths : {}",
118 make_stat(&bccx
, bccx
.stats
.stable_paths
));
121 fn make_stat(bccx
: &BorrowckCtxt
, stat
: usize) -> String
{
122 let total
= bccx
.stats
.guaranteed_paths
as f64;
123 let perc
= if total
== 0.0 { 0.0 }
else { stat as f64 * 100.0 / total }
;
124 format
!("{} ({:.0}%)", stat
, perc
)
128 fn borrowck_item(this
: &mut BorrowckCtxt
, item
: &ast
::Item
) {
129 // Gather loans for items. Note that we don't need
130 // to check loans for single expressions. The check
131 // loan step is intended for things that have a data
132 // flow dependent conditions.
134 ast
::ItemStatic(_
, _
, ref ex
) |
135 ast
::ItemConst(_
, ref ex
) => {
136 gather_loans
::gather_loans_in_static_initializer(this
, &**ex
);
141 visit
::walk_item(this
, item
);
144 /// Collection of conclusions determined via borrow checker analyses.
145 pub struct AnalysisData
<'a
, 'tcx
: 'a
> {
146 pub all_loans
: Vec
<Loan
<'tcx
>>,
147 pub loans
: DataFlowContext
<'a
, 'tcx
, LoanDataFlowOperator
>,
148 pub move_data
: move_data
::FlowedMoveData
<'a
, 'tcx
>,
151 fn borrowck_fn(this
: &mut BorrowckCtxt
,
157 debug
!("borrowck_fn(id={})", id
);
158 let cfg
= cfg
::CFG
::new(this
.tcx
, body
);
159 let AnalysisData
{ all_loans
,
161 move_data
: flowed_moves
} =
162 build_borrowck_dataflow_data(this
, fk
, decl
, &cfg
, body
, sp
, id
);
164 move_data
::fragments
::instrument_move_fragments(&flowed_moves
.move_data
,
168 move_data
::fragments
::build_unfragmented_map(this
,
169 &flowed_moves
.move_data
,
172 check_loans
::check_loans(this
,
180 visit
::walk_fn(this
, fk
, decl
, body
, sp
);
183 fn build_borrowck_dataflow_data
<'a
, 'tcx
>(this
: &mut BorrowckCtxt
<'a
, 'tcx
>,
190 -> AnalysisData
<'a
, 'tcx
>
192 // Check the body of fn items.
193 let id_range
= ast_util
::compute_id_range_for_fn_body(fk
, decl
, body
, sp
, id
);
194 let (all_loans
, move_data
) =
195 gather_loans
::gather_loans_in_fn(this
, id
, decl
, body
);
198 DataFlowContext
::new(this
.tcx
,
202 LoanDataFlowOperator
,
205 for (loan_idx
, loan
) in all_loans
.iter().enumerate() {
206 loan_dfcx
.add_gen(loan
.gen_scope
.node_id(), loan_idx
);
207 loan_dfcx
.add_kill(KillFrom
::ScopeEnd
, loan
.kill_scope
.node_id(), loan_idx
);
209 loan_dfcx
.add_kills_from_flow_exits(cfg
);
210 loan_dfcx
.propagate(cfg
, body
);
212 let flowed_moves
= move_data
::FlowedMoveData
::new(move_data
,
219 AnalysisData
{ all_loans
: all_loans
,
221 move_data
:flowed_moves
}
224 /// This and a `ty::ctxt` is all you need to run the dataflow analyses
225 /// used in the borrow checker.
226 pub struct FnPartsWithCFG
<'a
> {
227 pub fn_parts
: FnParts
<'a
>,
228 pub cfg
: &'a cfg
::CFG
,
231 impl<'a
> FnPartsWithCFG
<'a
> {
232 pub fn from_fn_like(f
: &'a FnLikeNode
,
233 g
: &'a cfg
::CFG
) -> FnPartsWithCFG
<'a
> {
234 FnPartsWithCFG { fn_parts: f.to_fn_parts(), cfg: g }
238 /// Accessor for introspective clients inspecting `AnalysisData` and
239 /// the `BorrowckCtxt` itself , e.g. the flowgraph visualizer.
240 pub fn build_borrowck_dataflow_data_for_fn
<'a
, 'tcx
>(
241 tcx
: &'a ty
::ctxt
<'tcx
>,
242 input
: FnPartsWithCFG
<'a
>)
243 -> (BorrowckCtxt
<'a
, 'tcx
>, AnalysisData
<'a
, 'tcx
>)
246 let mut bccx
= BorrowckCtxt
{
248 free_region_map
: FreeRegionMap
::new(),
250 loaned_paths_same
: 0,
257 let p
= input
.fn_parts
;
259 let dataflow_data
= build_borrowck_dataflow_data(&mut bccx
,
267 (bccx
, dataflow_data
)
270 // ----------------------------------------------------------------------
273 pub struct BorrowckCtxt
<'a
, 'tcx
: 'a
> {
274 tcx
: &'a ty
::ctxt
<'tcx
>,
276 // Hacky. As we visit various fns, we have to load up the
277 // free-region map for each one. This map is computed by during
278 // typeck for each fn item and stored -- closures just use the map
279 // from the fn item that encloses them. Since we walk the fns in
280 // order, we basically just overwrite this field as we enter a fn
281 // item and restore it afterwards in a stack-like fashion. Then
282 // the borrow checking code can assume that `free_region_map` is
283 // always the correct map for the current fn. Feels like it'd be
284 // better to just recompute this, rather than store it, but it's a
285 // bit of a pain to factor that code out at the moment.
286 free_region_map
: FreeRegionMap
,
293 loaned_paths_same
: usize,
294 loaned_paths_imm
: usize,
296 guaranteed_paths
: usize
299 pub type BckResult
<'tcx
, T
> = Result
<T
, BckError
<'tcx
>>;
301 ///////////////////////////////////////////////////////////////////////////
302 // Loans and loan paths
304 /// Record of a loan that was issued.
305 pub struct Loan
<'tcx
> {
307 loan_path
: Rc
<LoanPath
<'tcx
>>,
308 kind
: ty
::BorrowKind
,
309 restricted_paths
: Vec
<Rc
<LoanPath
<'tcx
>>>,
311 /// gen_scope indicates where loan is introduced. Typically the
312 /// loan is introduced at the point of the borrow, but in some
313 /// cases, notably method arguments, the loan may be introduced
314 /// only later, once it comes into scope. See also
315 /// `GatherLoanCtxt::compute_gen_scope`.
316 gen_scope
: region
::CodeExtent
,
318 /// kill_scope indicates when the loan goes out of scope. This is
319 /// either when the lifetime expires or when the local variable
320 /// which roots the loan-path goes out of scope, whichever happens
321 /// faster. See also `GatherLoanCtxt::compute_kill_scope`.
322 kill_scope
: region
::CodeExtent
,
324 cause
: euv
::LoanCause
,
327 impl<'tcx
> Loan
<'tcx
> {
328 pub fn loan_path(&self) -> Rc
<LoanPath
<'tcx
>> {
329 self.loan_path
.clone()
334 pub struct LoanPath
<'tcx
> {
335 kind
: LoanPathKind
<'tcx
>,
339 impl<'tcx
> PartialEq
for LoanPath
<'tcx
> {
340 fn eq(&self, that
: &LoanPath
<'tcx
>) -> bool
{
341 let r
= self.kind
== that
.kind
;
342 debug_assert
!(self.ty
== that
.ty
|| !r
,
343 "Somehow loan paths are equal though their tys are not.");
348 #[derive(PartialEq, Eq, Hash, Debug)]
349 pub enum LoanPathKind
<'tcx
> {
350 LpVar(ast
::NodeId
), // `x` in README.md
351 LpUpvar(ty
::UpvarId
), // `x` captured by-value into closure
352 LpDowncast(Rc
<LoanPath
<'tcx
>>, ast
::DefId
), // `x` downcast to particular enum variant
353 LpExtend(Rc
<LoanPath
<'tcx
>>, mc
::MutabilityCategory
, LoanPathElem
)
356 impl<'tcx
> LoanPath
<'tcx
> {
357 fn new(kind
: LoanPathKind
<'tcx
>, ty
: ty
::Ty
<'tcx
>) -> LoanPath
<'tcx
> {
358 LoanPath { kind: kind, ty: ty }
361 fn to_type(&self) -> ty
::Ty
<'tcx
> { self.ty }
364 // FIXME (pnkfelix): See discussion here
365 // https://github.com/pnkfelix/rust/commit/
366 // b2b39e8700e37ad32b486b9a8409b50a8a53aa51#commitcomment-7892003
367 const DOWNCAST_PRINTED_OPERATOR
: &'
static str = " as ";
369 // A local, "cleaned" version of `mc::InteriorKind` that drops
370 // information that is not relevant to loan-path analysis. (In
371 // particular, the distinction between how precisely a array-element
372 // is tracked is irrelevant here.)
373 #[derive(Clone, Copy, PartialEq, Eq, Hash)]
374 pub enum InteriorKind
{
375 InteriorField(mc
::FieldName
),
376 InteriorElement(mc
::ElementKind
),
379 trait ToInteriorKind { fn cleaned(self) -> InteriorKind; }
380 impl ToInteriorKind
for mc
::InteriorKind
{
381 fn cleaned(self) -> InteriorKind
{
383 mc
::InteriorField(name
) => InteriorField(name
),
384 mc
::InteriorElement(_
, elem_kind
) => InteriorElement(elem_kind
),
389 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
390 pub enum LoanPathElem
{
391 LpDeref(mc
::PointerKind
), // `*LV` in README.md
392 LpInterior(InteriorKind
), // `LV.f` in README.md
395 pub fn closure_to_block(closure_id
: ast
::NodeId
,
396 tcx
: &ty
::ctxt
) -> ast
::NodeId
{
397 match tcx
.map
.get(closure_id
) {
398 ast_map
::NodeExpr(expr
) => match expr
.node
{
399 ast
::ExprClosure(_
, _
, ref block
) => {
403 panic
!("encountered non-closure id: {}", closure_id
)
406 _
=> panic
!("encountered non-expr id: {}", closure_id
)
410 impl<'tcx
> LoanPath
<'tcx
> {
411 pub fn kill_scope(&self, tcx
: &ty
::ctxt
<'tcx
>) -> region
::CodeExtent
{
413 LpVar(local_id
) => tcx
.region_maps
.var_scope(local_id
),
414 LpUpvar(upvar_id
) => {
415 let block_id
= closure_to_block(upvar_id
.closure_expr_id
, tcx
);
416 region
::CodeExtent
::from_node_id(block_id
)
418 LpDowncast(ref base
, _
) |
419 LpExtend(ref base
, _
, _
) => base
.kill_scope(tcx
),
423 fn has_fork(&self, other
: &LoanPath
<'tcx
>) -> bool
{
424 match (&self.kind
, &other
.kind
) {
425 (&LpExtend(ref base
, _
, LpInterior(id
)), &LpExtend(ref base2
, _
, LpInterior(id2
))) =>
427 base
.has_fork(&**base2
)
431 (&LpExtend(ref base
, _
, LpDeref(_
)), _
) => base
.has_fork(other
),
432 (_
, &LpExtend(ref base
, _
, LpDeref(_
))) => self.has_fork(&**base
),
437 fn depth(&self) -> usize {
439 LpExtend(ref base
, _
, LpDeref(_
)) => base
.depth(),
440 LpExtend(ref base
, _
, LpInterior(_
)) => base
.depth() + 1,
445 fn common(&self, other
: &LoanPath
<'tcx
>) -> Option
<LoanPath
<'tcx
>> {
446 match (&self.kind
, &other
.kind
) {
447 (&LpExtend(ref base
, a
, LpInterior(id
)),
448 &LpExtend(ref base2
, _
, LpInterior(id2
))) => {
450 base
.common(&**base2
).map(|x
| {
452 if base
.depth() == xd
&& base2
.depth() == xd
{
453 assert_eq
!(base
.ty
, base2
.ty
);
454 assert_eq
!(self.ty
, other
.ty
);
456 kind
: LpExtend(Rc
::new(x
), a
, LpInterior(id
)),
464 base
.common(&**base2
)
467 (&LpExtend(ref base
, _
, LpDeref(_
)), _
) => base
.common(other
),
468 (_
, &LpExtend(ref other
, _
, LpDeref(_
))) => self.common(&**other
),
469 (&LpVar(id
), &LpVar(id2
)) => {
471 assert_eq
!(self.ty
, other
.ty
);
472 Some(LoanPath { kind: LpVar(id), ty: self.ty }
)
477 (&LpUpvar(id
), &LpUpvar(id2
)) => {
479 assert_eq
!(self.ty
, other
.ty
);
480 Some(LoanPath { kind: LpUpvar(id), ty: self.ty }
)
490 pub fn opt_loan_path
<'tcx
>(cmt
: &mc
::cmt
<'tcx
>) -> Option
<Rc
<LoanPath
<'tcx
>>> {
491 //! Computes the `LoanPath` (if any) for a `cmt`.
492 //! Note that this logic is somewhat duplicated in
493 //! the method `compute()` found in `gather_loans::restrictions`,
494 //! which allows it to share common loan path pieces as it
495 //! traverses the CMT.
497 let new_lp
= |v
: LoanPathKind
<'tcx
>| Rc
::new(LoanPath
::new(v
, cmt
.ty
));
501 mc
::cat_static_item
=> {
505 mc
::cat_local(id
) => {
506 Some(new_lp(LpVar(id
)))
509 mc
::cat_upvar(mc
::Upvar { id, .. }
) => {
510 Some(new_lp(LpUpvar(id
)))
513 mc
::cat_deref(ref cmt_base
, _
, pk
) => {
514 opt_loan_path(cmt_base
).map(|lp
| {
515 new_lp(LpExtend(lp
, cmt
.mutbl
, LpDeref(pk
)))
519 mc
::cat_interior(ref cmt_base
, ik
) => {
520 opt_loan_path(cmt_base
).map(|lp
| {
521 new_lp(LpExtend(lp
, cmt
.mutbl
, LpInterior(ik
.cleaned())))
525 mc
::cat_downcast(ref cmt_base
, variant_def_id
) =>
526 opt_loan_path(cmt_base
)
528 new_lp(LpDowncast(lp
, variant_def_id
))
534 ///////////////////////////////////////////////////////////////////////////
537 // Errors that can occur
539 pub enum bckerr_code
{
541 err_out_of_scope(ty
::Region
, ty
::Region
), // superscope, subscope
542 err_borrowed_pointer_too_short(ty
::Region
, ty
::Region
), // loan, ptr
545 // Combination of an error code and the categorization of the expression
548 pub struct BckError
<'tcx
> {
550 cause
: AliasableViolationKind
,
555 #[derive(Copy, Clone, Debug, PartialEq)]
556 pub enum AliasableViolationKind
{
558 BorrowViolation(euv
::LoanCause
)
561 #[derive(Copy, Clone, Debug)]
562 pub enum MovedValueUseKind
{
567 ///////////////////////////////////////////////////////////////////////////
570 impl<'a
, 'tcx
> BorrowckCtxt
<'a
, 'tcx
> {
571 pub fn is_subregion_of(&self, r_sub
: ty
::Region
, r_sup
: ty
::Region
)
574 self.free_region_map
.is_subregion_of(self.tcx
, r_sub
, r_sup
)
577 pub fn report(&self, err
: BckError
<'tcx
>) {
578 // Catch and handle some particular cases.
579 match (&err
.code
, &err
.cause
) {
580 (&err_out_of_scope(ty
::ReScope(_
), ty
::ReStatic
),
581 &BorrowViolation(euv
::ClosureCapture(span
))) |
582 (&err_out_of_scope(ty
::ReScope(_
), ty
::ReFree(..)),
583 &BorrowViolation(euv
::ClosureCapture(span
))) => {
584 return self.report_out_of_scope_escaping_closure_capture(&err
, span
);
592 &self.bckerr_to_string(&err
));
593 self.note_and_explain_bckerr(err
);
596 pub fn report_use_of_moved_value
<'b
>(&self,
598 use_kind
: MovedValueUseKind
,
600 the_move
: &move_data
::Move
,
601 moved_lp
: &LoanPath
<'tcx
>,
602 param_env
: &ty
::ParameterEnvironment
<'b
,'tcx
>) {
603 let verb
= match use_kind
{
605 MovedInCapture
=> "capture",
608 let (ol
, moved_lp_msg
) = match the_move
.kind
{
609 move_data
::Declared
=> {
611 self.tcx
.sess
, use_span
, E0381
,
612 "{} of possibly uninitialized variable: `{}`",
614 self.loan_path_to_string(lp
));
616 (self.loan_path_to_string(moved_lp
),
620 // If moved_lp is something like `x.a`, and lp is something like `x.b`, we would
621 // normally generate a rather confusing message:
623 // error: use of moved value: `x.b`
624 // note: `x.a` moved here...
626 // What we want to do instead is get the 'common ancestor' of the two moves and
627 // use that for most of the message instead, giving is something like this:
629 // error: use of moved value: `x`
630 // note: `x` moved here (through moving `x.a`)...
632 let common
= moved_lp
.common(lp
);
633 let has_common
= common
.is_some();
634 let has_fork
= moved_lp
.has_fork(lp
);
635 let (nl
, ol
, moved_lp_msg
) =
636 if has_fork
&& has_common
{
637 let nl
= self.loan_path_to_string(&common
.unwrap());
639 let moved_lp_msg
= format
!(" (through moving `{}`)",
640 self.loan_path_to_string(moved_lp
));
641 (nl
, ol
, moved_lp_msg
)
643 (self.loan_path_to_string(lp
),
644 self.loan_path_to_string(moved_lp
),
648 let partial
= moved_lp
.depth() > lp
.depth();
649 let msg
= if !has_fork
&& partial { "partially " }
650 else if has_fork
&& !has_common { "collaterally "}
653 self.tcx
.sess
, use_span
, E0382
,
654 "{} of {}moved value: `{}`",
660 match the_move
.kind
{
661 move_data
::Declared
=> {}
663 move_data
::MoveExpr
=> {
664 let (expr_ty
, expr_span
) = match self.tcx
667 Some(ast_map
::NodeExpr(expr
)) => {
668 (self.tcx
.expr_ty_adjusted(&*expr
), expr
.span
)
671 self.tcx
.sess
.bug(&format
!("MoveExpr({}) maps to \
677 let (suggestion
, _
) =
678 move_suggestion(param_env
, expr_span
, expr_ty
, ("moved by default", ""));
679 // If the two spans are the same, it's because the expression will be evaluated
680 // multiple times. Avoid printing the same span and adjust the wording so it makes
681 // more sense that it's from multiple evalutations.
682 if expr_span
== use_span
{
684 &format
!("`{}` was previously moved here{} because it has type `{}`, \
691 self.tcx
.sess
.span_note(
693 &format
!("`{}` moved here{} because it has type `{}`, which is {}",
701 move_data
::MovePat
=> {
702 let pat_ty
= self.tcx
.node_id_to_type(the_move
.id
);
703 let span
= self.tcx
.map
.span(the_move
.id
);
704 self.tcx
.sess
.span_note(span
,
705 &format
!("`{}` moved here{} because it has type `{}`, \
706 which is moved by default",
710 match self.tcx
.sess
.codemap().span_to_snippet(span
) {
712 self.tcx
.sess
.span_suggestion(
714 &format
!("if you would like to borrow the value instead, \
715 use a `ref` binding as shown:"),
716 format
!("ref {}", string
));
719 self.tcx
.sess
.fileline_help(span
,
720 "use `ref` to override");
725 move_data
::Captured
=> {
726 let (expr_ty
, expr_span
) = match self.tcx
729 Some(ast_map
::NodeExpr(expr
)) => {
730 (self.tcx
.expr_ty_adjusted(&*expr
), expr
.span
)
733 self.tcx
.sess
.bug(&format
!("Captured({}) maps to \
739 let (suggestion
, help
) =
740 move_suggestion(param_env
,
744 "make a copy and capture that instead to override"));
745 self.tcx
.sess
.span_note(
747 &format
!("`{}` moved into closure environment here{} because it \
748 has type `{}`, which is {}",
753 self.tcx
.sess
.fileline_help(expr_span
, help
);
757 fn move_suggestion
<'a
,'tcx
>(param_env
: &ty
::ParameterEnvironment
<'a
,'tcx
>,
760 default_msgs
: (&'
static str, &'
static str))
761 -> (&'
static str, &'
static str) {
764 if ty
.moves_by_default(param_env
, span
) {
766 "perhaps you meant to use `clone()`?")
775 pub fn report_partial_reinitialization_of_uninitialized_structure(
778 lp
: &LoanPath
<'tcx
>) {
780 self.tcx
.sess
, span
, E0383
,
781 "partial reinitialization of uninitialized structure `{}`",
782 self.loan_path_to_string(lp
));
785 pub fn report_reassigned_immutable_variable(&self,
789 &move_data
::Assignment
) {
791 self.tcx
.sess
, span
, E0384
,
792 "re-assignment of immutable variable `{}`",
793 self.loan_path_to_string(lp
));
794 self.tcx
.sess
.span_note(assign
.span
, "prior assignment occurs here");
797 pub fn span_err(&self, s
: Span
, m
: &str) {
798 self.tcx
.sess
.span_err(s
, m
);
801 pub fn span_bug(&self, s
: Span
, m
: &str) {
802 self.tcx
.sess
.span_bug(s
, m
);
805 pub fn span_note(&self, s
: Span
, m
: &str) {
806 self.tcx
.sess
.span_note(s
, m
);
809 pub fn span_end_note(&self, s
: Span
, m
: &str) {
810 self.tcx
.sess
.span_end_note(s
, m
);
813 pub fn fileline_help(&self, s
: Span
, m
: &str) {
814 self.tcx
.sess
.fileline_help(s
, m
);
817 pub fn bckerr_to_string(&self, err
: &BckError
<'tcx
>) -> String
{
820 let descr
= match err
.cmt
.note
{
821 mc
::NoteClosureEnv(_
) | mc
::NoteUpvarRef(_
) => {
822 self.cmt_to_string(&*err
.cmt
)
824 _
=> match opt_loan_path(&err
.cmt
) {
827 err
.cmt
.mutbl
.to_user_str(),
828 self.cmt_to_string(&*err
.cmt
))
831 format
!("{} {} `{}`",
832 err
.cmt
.mutbl
.to_user_str(),
833 self.cmt_to_string(&*err
.cmt
),
834 self.loan_path_to_string(&*lp
))
840 MutabilityViolation
=> {
841 format
!("cannot assign to {}", descr
)
843 BorrowViolation(euv
::ClosureCapture(_
)) => {
844 format
!("closure cannot assign to {}", descr
)
846 BorrowViolation(euv
::OverloadedOperator
) |
847 BorrowViolation(euv
::AddrOf
) |
848 BorrowViolation(euv
::RefBinding
) |
849 BorrowViolation(euv
::AutoRef
) |
850 BorrowViolation(euv
::AutoUnsafe
) |
851 BorrowViolation(euv
::ForLoop
) |
852 BorrowViolation(euv
::MatchDiscriminant
) => {
853 format
!("cannot borrow {} as mutable", descr
)
855 BorrowViolation(euv
::ClosureInvocation
) => {
856 self.tcx
.sess
.span_bug(err
.span
,
857 "err_mutbl with a closure invocation");
861 err_out_of_scope(..) => {
862 let msg
= match opt_loan_path(&err
.cmt
) {
863 None
=> "borrowed value".to_string(),
865 format
!("`{}`", self.loan_path_to_string(&*lp
))
868 format
!("{} does not live long enough", msg
)
870 err_borrowed_pointer_too_short(..) => {
871 let descr
= self.cmt_to_path_or_string(&err
.cmt
);
872 format
!("lifetime of {} is too short to guarantee \
873 its contents can be safely reborrowed",
879 pub fn report_aliasability_violation(&self,
881 kind
: AliasableViolationKind
,
882 cause
: mc
::AliasableReason
) {
883 let mut is_closure
= false;
884 let prefix
= match kind
{
885 MutabilityViolation
=> {
886 "cannot assign to data"
888 BorrowViolation(euv
::ClosureCapture(_
)) |
889 BorrowViolation(euv
::OverloadedOperator
) |
890 BorrowViolation(euv
::AddrOf
) |
891 BorrowViolation(euv
::AutoRef
) |
892 BorrowViolation(euv
::AutoUnsafe
) |
893 BorrowViolation(euv
::RefBinding
) |
894 BorrowViolation(euv
::MatchDiscriminant
) => {
895 "cannot borrow data mutably"
898 BorrowViolation(euv
::ClosureInvocation
) => {
903 BorrowViolation(euv
::ForLoop
) => {
909 mc
::AliasableOther
=> {
911 self.tcx
.sess
, span
, E0385
,
912 "{} in an aliasable location", prefix
);
914 mc
::AliasableReason
::UnaliasableImmutable
=> {
916 self.tcx
.sess
, span
, E0386
,
917 "{} in an immutable container", prefix
);
919 mc
::AliasableClosure(id
) => {
921 self.tcx
.sess
, span
, E0387
,
922 "{} in a captured outer variable in an `Fn` closure", prefix
);
923 if let BorrowViolation(euv
::ClosureCapture(_
)) = kind
{
924 // The aliasability violation with closure captures can
925 // happen for nested closures, so we know the enclosing
926 // closure incorrectly accepts an `Fn` while it needs to
928 span_help
!(self.tcx
.sess
, self.tcx
.map
.span(id
),
929 "consider changing this to accept closures that implement `FnMut`");
931 span_help
!(self.tcx
.sess
, self.tcx
.map
.span(id
),
932 "consider changing this closure to take self by mutable reference");
935 mc
::AliasableStatic(..) |
936 mc
::AliasableStaticMut(..) => {
938 self.tcx
.sess
, span
, E0388
,
939 "{} in a static location", prefix
);
941 mc
::AliasableBorrowed
=> {
943 self.tcx
.sess
, span
, E0389
,
944 "{} in a `&` reference", prefix
);
949 self.tcx
.sess
.fileline_help(
951 "closures behind references must be called via `&mut`");
955 fn report_out_of_scope_escaping_closure_capture(&self,
956 err
: &BckError
<'tcx
>,
959 let cmt_path_or_string
= self.cmt_to_path_or_string(&err
.cmt
);
962 self.tcx
.sess
, err
.span
, E0373
,
963 "closure may outlive the current function, \
965 which is owned by the current function",
968 self.tcx
.sess
.span_note(
970 &format
!("{} is borrowed here",
971 cmt_path_or_string
));
974 match self.tcx
.sess
.codemap().span_to_snippet(err
.span
) {
975 Ok(string
) => format
!("move {}", string
),
976 Err(_
) => format
!("move |<args>| <body>")
979 self.tcx
.sess
.span_suggestion(
981 &format
!("to force the closure to take ownership of {} \
982 (and any other referenced variables), \
983 use the `move` keyword, as shown:",
988 pub fn note_and_explain_bckerr(&self, err
: BckError
<'tcx
>) {
993 mc
::NoteClosureEnv(upvar_id
) | mc
::NoteUpvarRef(upvar_id
) => {
994 // If this is an `Fn` closure, it simply can't mutate upvars.
995 // If it's an `FnMut` closure, the original variable was declared immutable.
996 // We need to determine which is the case here.
997 let kind
= match err
.cmt
.upvar().unwrap().cat
{
998 mc
::cat_upvar(mc
::Upvar { kind, .. }
) => kind
,
1001 if kind
== ty
::FnClosureKind
{
1002 self.tcx
.sess
.span_help(
1003 self.tcx
.map
.span(upvar_id
.closure_expr_id
),
1004 "consider changing this closure to take \
1005 self by mutable reference");
1012 err_out_of_scope(super_scope
, sub_scope
) => {
1013 self.tcx
.note_and_explain_region(
1014 "reference must be valid for ",
1017 self.tcx
.note_and_explain_region(
1018 "...but borrowed value is only valid for ",
1021 if let Some(span
) = statement_scope_span(self.tcx
, super_scope
) {
1022 self.tcx
.sess
.span_help(span
,
1023 "consider using a `let` binding to increase its lifetime");
1027 err_borrowed_pointer_too_short(loan_scope
, ptr_scope
) => {
1028 let descr
= match opt_loan_path(&err
.cmt
) {
1030 format
!("`{}`", self.loan_path_to_string(&*lp
))
1032 None
=> self.cmt_to_string(&*err
.cmt
),
1034 self.tcx
.note_and_explain_region(
1035 &format
!("{} would have to be valid for ",
1039 self.tcx
.note_and_explain_region(
1040 &format
!("...but {} is only valid for ", descr
),
1047 pub fn append_loan_path_to_string(&self,
1048 loan_path
: &LoanPath
<'tcx
>,
1050 match loan_path
.kind
{
1051 LpUpvar(ty
::UpvarId{ var_id: id, closure_expr_id: _ }
) |
1053 out
.push_str(&self.tcx
.local_var_name_str(id
));
1056 LpDowncast(ref lp_base
, variant_def_id
) => {
1058 self.append_loan_path_to_string(&**lp_base
, out
);
1059 out
.push_str(DOWNCAST_PRINTED_OPERATOR
);
1060 out
.push_str(&self.tcx
.item_path_str(variant_def_id
));
1065 LpExtend(ref lp_base
, _
, LpInterior(InteriorField(fname
))) => {
1066 self.append_autoderefd_loan_path_to_string(&**lp_base
, out
);
1068 mc
::NamedField(fname
) => {
1070 out
.push_str(&fname
.as_str());
1072 mc
::PositionalField(idx
) => {
1074 out
.push_str(&idx
.to_string());
1079 LpExtend(ref lp_base
, _
, LpInterior(InteriorElement(..))) => {
1080 self.append_autoderefd_loan_path_to_string(&**lp_base
, out
);
1081 out
.push_str("[..]");
1084 LpExtend(ref lp_base
, _
, LpDeref(_
)) => {
1086 self.append_loan_path_to_string(&**lp_base
, out
);
1091 pub fn append_autoderefd_loan_path_to_string(&self,
1092 loan_path
: &LoanPath
<'tcx
>,
1094 match loan_path
.kind
{
1095 LpExtend(ref lp_base
, _
, LpDeref(_
)) => {
1096 // For a path like `(*x).f` or `(*x)[3]`, autoderef
1097 // rules would normally allow users to omit the `*x`.
1098 // So just serialize such paths to `x.f` or x[3]` respectively.
1099 self.append_autoderefd_loan_path_to_string(&**lp_base
, out
)
1102 LpDowncast(ref lp_base
, variant_def_id
) => {
1104 self.append_autoderefd_loan_path_to_string(&**lp_base
, out
);
1106 out
.push_str(&self.tcx
.item_path_str(variant_def_id
));
1110 LpVar(..) | LpUpvar(..) | LpExtend(_
, _
, LpInterior(..)) => {
1111 self.append_loan_path_to_string(loan_path
, out
)
1116 pub fn loan_path_to_string(&self, loan_path
: &LoanPath
<'tcx
>) -> String
{
1117 let mut result
= String
::new();
1118 self.append_loan_path_to_string(loan_path
, &mut result
);
1122 pub fn cmt_to_string(&self, cmt
: &mc
::cmt_
<'tcx
>) -> String
{
1123 cmt
.descriptive_string(self.tcx
)
1126 pub fn cmt_to_path_or_string(&self, cmt
: &mc
::cmt
<'tcx
>) -> String
{
1127 match opt_loan_path(cmt
) {
1128 Some(lp
) => format
!("`{}`", self.loan_path_to_string(&lp
)),
1129 None
=> self.cmt_to_string(cmt
),
1134 fn statement_scope_span(tcx
: &ty
::ctxt
, region
: ty
::Region
) -> Option
<Span
> {
1136 ty
::ReScope(scope
) => {
1137 match tcx
.map
.find(scope
.node_id()) {
1138 Some(ast_map
::NodeStmt(stmt
)) => Some(stmt
.span
),
1146 impl BitwiseOperator
for LoanDataFlowOperator
{
1148 fn join(&self, succ
: usize, pred
: usize) -> usize {
1149 succ
| pred
// loans from both preds are in scope
1153 impl DataFlowOperator
for LoanDataFlowOperator
{
1155 fn initial_value(&self) -> bool
{
1156 false // no loans in scope by default
1160 impl<'tcx
> fmt
::Debug
for InteriorKind
{
1161 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1163 InteriorField(mc
::NamedField(fld
)) => write
!(f
, "{}", fld
),
1164 InteriorField(mc
::PositionalField(i
)) => write
!(f
, "#{}", i
),
1165 InteriorElement(..) => write
!(f
, "[]"),
1170 impl<'tcx
> fmt
::Debug
for Loan
<'tcx
> {
1171 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1172 write
!(f
, "Loan_{}({:?}, {:?}, {:?}-{:?}, {:?})",
1178 self.restricted_paths
)
1182 impl<'tcx
> fmt
::Debug
for LoanPath
<'tcx
> {
1183 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1186 write
!(f
, "$({})", ty
::tls
::with(|tcx
| tcx
.map
.node_to_string(id
)))
1189 LpUpvar(ty
::UpvarId{ var_id, closure_expr_id }
) => {
1190 let s
= ty
::tls
::with(|tcx
| tcx
.map
.node_to_string(var_id
));
1191 write
!(f
, "$({} captured by id={})", s
, closure_expr_id
)
1194 LpDowncast(ref lp
, variant_def_id
) => {
1195 let variant_str
= if variant_def_id
.krate
== ast
::LOCAL_CRATE
{
1196 ty
::tls
::with(|tcx
| tcx
.item_path_str(variant_def_id
))
1198 format
!("{:?}", variant_def_id
)
1200 write
!(f
, "({:?}{}{})", lp
, DOWNCAST_PRINTED_OPERATOR
, variant_str
)
1203 LpExtend(ref lp
, _
, LpDeref(_
)) => {
1204 write
!(f
, "{:?}.*", lp
)
1207 LpExtend(ref lp
, _
, LpInterior(ref interior
)) => {
1208 write
!(f
, "{:?}.{:?}", lp
, interior
)
1214 impl<'tcx
> fmt
::Display
for LoanPath
<'tcx
> {
1215 fn fmt(&self, f
: &mut fmt
::Formatter
) -> fmt
::Result
{
1218 write
!(f
, "$({})", ty
::tls
::with(|tcx
| tcx
.map
.node_to_user_string(id
)))
1221 LpUpvar(ty
::UpvarId{ var_id, closure_expr_id: _ }
) => {
1222 let s
= ty
::tls
::with(|tcx
| tcx
.map
.node_to_user_string(var_id
));
1223 write
!(f
, "$({} captured by closure)", s
)
1226 LpDowncast(ref lp
, variant_def_id
) => {
1227 let variant_str
= if variant_def_id
.krate
== ast
::LOCAL_CRATE
{
1228 ty
::tls
::with(|tcx
| tcx
.item_path_str(variant_def_id
))
1230 format
!("{:?}", variant_def_id
)
1232 write
!(f
, "({}{}{})", lp
, DOWNCAST_PRINTED_OPERATOR
, variant_str
)
1235 LpExtend(ref lp
, _
, LpDeref(_
)) => {
1236 write
!(f
, "{}.*", lp
)
1239 LpExtend(ref lp
, _
, LpInterior(ref interior
)) => {
1240 write
!(f
, "{}.{:?}", lp
, interior
)