1 // Copyright 2017 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 //! This query borrow-checks the MIR to (further) ensure it is not broken.
14 use rustc
::hir
::def_id
::{DefId}
;
15 use rustc
::infer
::{InferCtxt}
;
16 use rustc
::ty
::{self, TyCtxt, ParamEnv}
;
17 use rustc
::ty
::maps
::Providers
;
18 use rustc
::mir
::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue, Local}
;
19 use rustc
::mir
::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue}
;
20 use rustc
::mir
::{Statement, StatementKind, Terminator, TerminatorKind}
;
23 use rustc_data_structures
::fx
::FxHashSet
;
24 use rustc_data_structures
::indexed_set
::{self, IdxSetBuf}
;
25 use rustc_data_structures
::indexed_vec
::{Idx}
;
27 use syntax
::ast
::{self}
;
28 use syntax_pos
::{DUMMY_SP, Span}
;
30 use dataflow
::{do_dataflow}
;
31 use dataflow
::{MoveDataParamEnv}
;
32 use dataflow
::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer}
;
33 use dataflow
::{MaybeInitializedLvals, MaybeUninitializedLvals}
;
34 use dataflow
::{MovingOutStatements}
;
35 use dataflow
::{Borrows, BorrowData, BorrowIndex}
;
36 use dataflow
::move_paths
::{MoveError, IllegalMoveOriginKind}
;
37 use dataflow
::move_paths
::{HasMoveData, MoveData, MovePathIndex, LookupResult, MoveOutIndex}
;
38 use util
::borrowck_errors
::{BorrowckErrors, Origin}
;
40 use self::MutateMode
::{JustWrite, WriteAndRead}
;
41 use self::ConsumeKind
::{Consume}
;
44 pub fn provide(providers
: &mut Providers
) {
45 *providers
= Providers
{
51 fn mir_borrowck
<'a
, 'tcx
>(tcx
: TyCtxt
<'a
, 'tcx
, 'tcx
>, def_id
: DefId
) {
52 let input_mir
= tcx
.mir_validated(def_id
);
53 debug
!("run query mir_borrowck: {}", tcx
.item_path_str(def_id
));
56 !tcx
.has_attr(def_id
, "rustc_mir_borrowck") &&
57 !tcx
.sess
.opts
.debugging_opts
.borrowck_mir
&&
58 !tcx
.sess
.opts
.debugging_opts
.nll
63 tcx
.infer_ctxt().enter(|infcx
| {
64 let input_mir
: &Mir
= &input_mir
.borrow();
65 do_mir_borrowck(&infcx
, input_mir
, def_id
);
67 debug
!("mir_borrowck done");
70 fn do_mir_borrowck
<'a
, 'gcx
, 'tcx
>(infcx
: &InferCtxt
<'a
, 'gcx
, 'tcx
>,
71 input_mir
: &Mir
<'gcx
>,
75 let attributes
= tcx
.get_attrs(def_id
);
76 let param_env
= tcx
.param_env(def_id
);
77 let id
= tcx
.hir
.as_local_node_id(def_id
)
78 .expect("do_mir_borrowck: non-local DefId");
80 let move_data
: MoveData
<'tcx
> = match MoveData
::gather_moves(input_mir
, tcx
, param_env
) {
81 Ok(move_data
) => move_data
,
82 Err((move_data
, move_errors
)) => {
83 for move_error
in move_errors
{
84 let (span
, kind
): (Span
, IllegalMoveOriginKind
) = match move_error
{
85 MoveError
::UnionMove { .. }
=>
86 unimplemented
!("dont know how to report union move errors yet."),
87 MoveError
::IllegalMove { cannot_move_out_of: o }
=> (o
.span
, o
.kind
),
89 let origin
= Origin
::Mir
;
90 let mut err
= match kind
{
91 IllegalMoveOriginKind
::Static
=>
92 tcx
.cannot_move_out_of(span
, "static item", origin
),
93 IllegalMoveOriginKind
::BorrowedContent
=>
94 tcx
.cannot_move_out_of(span
, "borrowed_content", origin
),
95 IllegalMoveOriginKind
::InteriorOfTypeWithDestructor { container_ty: ty }
=>
96 tcx
.cannot_move_out_of_interior_of_drop(span
, ty
, origin
),
97 IllegalMoveOriginKind
::InteriorOfSliceOrArray { ty, is_index }
=>
98 tcx
.cannot_move_out_of_interior_noncopy(span
, ty
, is_index
, origin
),
106 // Make our own copy of the MIR. This copy will be modified (in place) to
107 // contain non-lexical lifetimes. It will have a lifetime tied
108 // to the inference context.
109 let mut mir
: Mir
<'tcx
> = input_mir
.clone();
112 // If we are in non-lexical mode, compute the non-lexical lifetimes.
113 let opt_regioncx
= if !tcx
.sess
.opts
.debugging_opts
.nll
{
116 Some(nll
::compute_regions(infcx
, def_id
, param_env
, mir
))
119 let mdpe
= MoveDataParamEnv { move_data: move_data, param_env: param_env }
;
120 let dead_unwinds
= IdxSetBuf
::new_empty(mir
.basic_blocks().len());
121 let flow_borrows
= do_dataflow(tcx
, mir
, id
, &attributes
, &dead_unwinds
,
122 Borrows
::new(tcx
, mir
, opt_regioncx
.as_ref()),
123 |bd
, i
| bd
.location(i
));
124 let flow_inits
= do_dataflow(tcx
, mir
, id
, &attributes
, &dead_unwinds
,
125 MaybeInitializedLvals
::new(tcx
, mir
, &mdpe
),
126 |bd
, i
| &bd
.move_data().move_paths
[i
]);
127 let flow_uninits
= do_dataflow(tcx
, mir
, id
, &attributes
, &dead_unwinds
,
128 MaybeUninitializedLvals
::new(tcx
, mir
, &mdpe
),
129 |bd
, i
| &bd
.move_data().move_paths
[i
]);
130 let flow_move_outs
= do_dataflow(tcx
, mir
, id
, &attributes
, &dead_unwinds
,
131 MovingOutStatements
::new(tcx
, mir
, &mdpe
),
132 |bd
, i
| &bd
.move_data().moves
[i
]);
134 let mut mbcx
= MirBorrowckCtxt
{
138 move_data
: &mdpe
.move_data
,
139 param_env
: param_env
,
140 storage_drop_or_dead_error_reported
: FxHashSet(),
143 let mut state
= InProgress
::new(flow_borrows
,
148 mbcx
.analyze_results(&mut state
); // entry point for DataflowResultsConsumer
152 pub struct MirBorrowckCtxt
<'cx
, 'gcx
: 'tcx
, 'tcx
: 'cx
> {
153 tcx
: TyCtxt
<'cx
, 'gcx
, 'tcx
>,
155 node_id
: ast
::NodeId
,
156 move_data
: &'cx MoveData
<'tcx
>,
157 param_env
: ParamEnv
<'gcx
>,
158 /// This field keeps track of when storage drop or dead errors are reported
159 /// in order to stop duplicate error reporting and identify the conditions required
160 /// for a "temporary value dropped here while still borrowed" error. See #45360.
161 storage_drop_or_dead_error_reported
: FxHashSet
<Local
>,
164 // (forced to be `pub` due to its use as an associated type below.)
165 pub struct InProgress
<'b
, 'gcx
: 'tcx
, 'tcx
: 'b
> {
166 borrows
: FlowInProgress
<Borrows
<'b
, 'gcx
, 'tcx
>>,
167 inits
: FlowInProgress
<MaybeInitializedLvals
<'b
, 'gcx
, 'tcx
>>,
168 uninits
: FlowInProgress
<MaybeUninitializedLvals
<'b
, 'gcx
, 'tcx
>>,
169 move_outs
: FlowInProgress
<MovingOutStatements
<'b
, 'gcx
, 'tcx
>>,
172 struct FlowInProgress
<BD
> where BD
: BitDenotation
{
173 base_results
: DataflowResults
<BD
>,
174 curr_state
: IdxSetBuf
<BD
::Idx
>,
175 stmt_gen
: IdxSetBuf
<BD
::Idx
>,
176 stmt_kill
: IdxSetBuf
<BD
::Idx
>,
180 // 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
181 // 2. loans made in overlapping scopes do not conflict
182 // 3. assignments do not affect things loaned out as immutable
183 // 4. moves do not affect things loaned out in any way
184 impl<'cx
, 'gcx
, 'tcx
> DataflowResultsConsumer
<'cx
, 'tcx
> for MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
185 type FlowState
= InProgress
<'cx
, 'gcx
, 'tcx
>;
187 fn mir(&self) -> &'cx Mir
<'tcx
> { self.mir }
189 fn reset_to_entry_of(&mut self, bb
: BasicBlock
, flow_state
: &mut Self::FlowState
) {
190 flow_state
.each_flow(|b
| b
.reset_to_entry_of(bb
),
191 |i
| i
.reset_to_entry_of(bb
),
192 |u
| u
.reset_to_entry_of(bb
),
193 |m
| m
.reset_to_entry_of(bb
));
196 fn reconstruct_statement_effect(&mut self,
198 flow_state
: &mut Self::FlowState
) {
199 flow_state
.each_flow(|b
| b
.reconstruct_statement_effect(location
),
200 |i
| i
.reconstruct_statement_effect(location
),
201 |u
| u
.reconstruct_statement_effect(location
),
202 |m
| m
.reconstruct_statement_effect(location
));
205 fn apply_local_effect(&mut self,
207 flow_state
: &mut Self::FlowState
) {
208 flow_state
.each_flow(|b
| b
.apply_local_effect(),
209 |i
| i
.apply_local_effect(),
210 |u
| u
.apply_local_effect(),
211 |m
| m
.apply_local_effect());
214 fn reconstruct_terminator_effect(&mut self,
216 flow_state
: &mut Self::FlowState
) {
217 flow_state
.each_flow(|b
| b
.reconstruct_terminator_effect(location
),
218 |i
| i
.reconstruct_terminator_effect(location
),
219 |u
| u
.reconstruct_terminator_effect(location
),
220 |m
| m
.reconstruct_terminator_effect(location
));
223 fn visit_block_entry(&mut self,
225 flow_state
: &Self::FlowState
) {
226 let summary
= flow_state
.summary();
227 debug
!("MirBorrowckCtxt::process_block({:?}): {}", bb
, summary
);
230 fn visit_statement_entry(&mut self,
232 stmt
: &Statement
<'tcx
>,
233 flow_state
: &Self::FlowState
) {
234 let summary
= flow_state
.summary();
235 debug
!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location
, stmt
, summary
);
236 let span
= stmt
.source_info
.span
;
238 StatementKind
::Assign(ref lhs
, ref rhs
) => {
239 // NOTE: NLL RFC calls for *shallow* write; using Deep
240 // for short-term compat w/ AST-borrowck. Also, switch
241 // to shallow requires to dataflow: "if this is an
242 // assignment `lv = <rvalue>`, then any loan for some
243 // path P of which `lv` is a prefix is killed."
244 self.mutate_lvalue(ContextKind
::AssignLhs
.new(location
),
245 (lhs
, span
), Deep
, JustWrite
, flow_state
);
247 self.consume_rvalue(ContextKind
::AssignRhs
.new(location
),
248 (rhs
, span
), location
, flow_state
);
250 StatementKind
::SetDiscriminant { ref lvalue, variant_index: _ }
=> {
251 self.mutate_lvalue(ContextKind
::SetDiscrim
.new(location
),
253 Shallow(Some(ArtificialField
::Discriminant
)),
257 StatementKind
::InlineAsm { ref asm, ref outputs, ref inputs }
=> {
258 for (o
, output
) in asm
.outputs
.iter().zip(outputs
) {
260 self.consume_lvalue(ContextKind
::InlineAsm
.new(location
),
265 self.mutate_lvalue(ContextKind
::InlineAsm
.new(location
),
268 if o
.is_rw { WriteAndRead }
else { JustWrite }
,
272 for input
in inputs
{
273 self.consume_operand(ContextKind
::InlineAsm
.new(location
),
275 (input
, span
), flow_state
);
278 StatementKind
::EndRegion(ref _rgn
) => {
279 // ignored when consuming results (update to
280 // flow_state already handled).
283 StatementKind
::Validate(..) |
284 StatementKind
::StorageLive(..) => {
285 // `Nop`, `Validate`, and `StorageLive` are irrelevant
289 StatementKind
::StorageDead(local
) => {
290 if !self.storage_drop_or_dead_error_reported
.contains(&local
) {
291 let error_reported
= self.access_lvalue(ContextKind
::StorageDead
.new(location
),
292 (&Lvalue
::Local(local
), span
),
293 (Shallow(None
), Write(WriteKind
::StorageDeadOrDrop
)), flow_state
);
296 self.storage_drop_or_dead_error_reported
.insert(local
);
303 fn visit_terminator_entry(&mut self,
305 term
: &Terminator
<'tcx
>,
306 flow_state
: &Self::FlowState
) {
308 let summary
= flow_state
.summary();
309 debug
!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", location
, term
, summary
);
310 let span
= term
.source_info
.span
;
312 TerminatorKind
::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ }
=> {
313 self.consume_operand(ContextKind
::SwitchInt
.new(loc
),
315 (discr
, span
), flow_state
);
317 TerminatorKind
::Drop { location: ref drop_lvalue, target: _, unwind: _ }
=> {
318 self.consume_lvalue(ContextKind
::Drop
.new(loc
),
320 (drop_lvalue
, span
), flow_state
);
322 TerminatorKind
::DropAndReplace
{ location
: ref drop_lvalue
,
323 value
: ref new_value
,
326 self.mutate_lvalue(ContextKind
::DropAndReplace
.new(loc
),
331 self.consume_operand(ContextKind
::DropAndReplace
.new(loc
),
333 (new_value
, span
), flow_state
);
335 TerminatorKind
::Call { ref func, ref args, ref destination, cleanup: _ }
=> {
336 self.consume_operand(ContextKind
::CallOperator
.new(loc
),
338 (func
, span
), flow_state
);
340 self.consume_operand(ContextKind
::CallOperand
.new(loc
),
342 (arg
, span
), flow_state
);
344 if let Some((ref dest
, _
/*bb*/)) = *destination
{
345 self.mutate_lvalue(ContextKind
::CallDest
.new(loc
),
352 TerminatorKind
::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ }
=> {
353 self.consume_operand(ContextKind
::Assert
.new(loc
),
355 (cond
, span
), flow_state
);
357 AssertMessage
::BoundsCheck { ref len, ref index }
=> {
358 self.consume_operand(ContextKind
::Assert
.new(loc
),
360 (len
, span
), flow_state
);
361 self.consume_operand(ContextKind
::Assert
.new(loc
),
363 (index
, span
), flow_state
);
365 AssertMessage
::Math(_
/*const_math_err*/) => {}
366 AssertMessage
::GeneratorResumedAfterReturn
=> {}
367 AssertMessage
::GeneratorResumedAfterPanic
=> {}
371 TerminatorKind
::Yield { ref value, resume: _, drop: _}
=> {
372 self.consume_operand(ContextKind
::Yield
.new(loc
),
373 Consume
, (value
, span
), flow_state
);
376 TerminatorKind
::Goto { target: _ }
|
377 TerminatorKind
::Resume
|
378 TerminatorKind
::Return
|
379 TerminatorKind
::GeneratorDrop
|
380 TerminatorKind
::Unreachable
|
381 TerminatorKind
::FalseEdges { .. }
=> {
382 // no data used, thus irrelevant to borrowck
388 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
389 enum MutateMode { JustWrite, WriteAndRead }
391 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
392 enum ConsumeKind { Drop, Consume }
394 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
395 enum Control { Continue, Break }
397 use self::ShallowOrDeep
::{Shallow, Deep}
;
398 use self::ReadOrWrite
::{Read, Write}
;
400 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
401 enum ArtificialField
{
406 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
408 /// From the RFC: "A *shallow* access means that the immediate
409 /// fields reached at LV are accessed, but references or pointers
410 /// found within are not dereferenced. Right now, the only access
411 /// that is shallow is an assignment like `x = ...;`, which would
412 /// be a *shallow write* of `x`."
413 Shallow(Option
<ArtificialField
>),
415 /// From the RFC: "A *deep* access means that all data reachable
416 /// through the given lvalue may be invalidated or accesses by
421 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
423 /// From the RFC: "A *read* means that the existing data may be
424 /// read, but will not be changed."
427 /// From the RFC: "A *write* means that the data may be mutated to
428 /// new values or otherwise invalidated (for example, it could be
429 /// de-initialized, as in a move operation).
433 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
439 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
442 MutableBorrow(BorrowKind
),
447 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
448 /// Checks an access to the given lvalue to see if it is allowed. Examines the set of borrows
449 /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
450 /// lvalue is initialized and (b) it is not borrowed in some way that would prevent this
453 /// Returns true if an error is reported, false otherwise.
454 fn access_lvalue(&mut self,
456 lvalue_span
: (&Lvalue
<'tcx
>, Span
),
457 kind
: (ShallowOrDeep
, ReadOrWrite
),
458 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>) -> bool
{
462 self.check_access_permissions(lvalue_span
, rw
);
464 let mut error_reported
= false;
465 self.each_borrow_involving_path(
466 context
, (sd
, lvalue_span
.0), flow_state
, |this
, _index
, borrow
, common_prefix
| {
467 match (rw
, borrow
.kind
) {
468 (Read(_
), BorrowKind
::Shared
) => {
471 (Read(kind
), BorrowKind
::Unique
) |
472 (Read(kind
), BorrowKind
::Mut
) => {
475 error_reported
= true;
476 this
.report_use_while_mutably_borrowed(
477 context
, lvalue_span
, borrow
)
479 ReadKind
::Borrow(bk
) => {
480 let end_issued_loan_span
=
481 flow_state
.borrows
.base_results
.operator().opt_region_end_span(
483 error_reported
= true;
484 this
.report_conflicting_borrow(
485 context
, common_prefix
, lvalue_span
, bk
,
486 &borrow
, end_issued_loan_span
)
491 (Write(kind
), _
) => {
493 WriteKind
::MutableBorrow(bk
) => {
494 let end_issued_loan_span
=
495 flow_state
.borrows
.base_results
.operator().opt_region_end_span(
497 error_reported
= true;
498 this
.report_conflicting_borrow(
499 context
, common_prefix
, lvalue_span
, bk
,
500 &borrow
, end_issued_loan_span
)
502 WriteKind
::StorageDeadOrDrop
=> {
504 flow_state
.borrows
.base_results
.operator().opt_region_end_span(
506 error_reported
= true;
507 this
.report_borrowed_value_does_not_live_long_enough(
508 context
, lvalue_span
, end_span
)
510 WriteKind
::Mutate
=> {
511 error_reported
= true;
512 this
.report_illegal_mutation_of_borrowed(
513 context
, lvalue_span
, borrow
)
516 error_reported
= true;
517 this
.report_move_out_while_borrowed(
518 context
, lvalue_span
, &borrow
)
528 fn mutate_lvalue(&mut self,
530 lvalue_span
: (&Lvalue
<'tcx
>, Span
),
533 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>) {
534 // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
536 MutateMode
::WriteAndRead
=> {
537 self.check_if_path_is_moved(context
, "update", lvalue_span
, flow_state
);
539 MutateMode
::JustWrite
=> {
540 self.check_if_assigned_path_is_moved(context
, lvalue_span
, flow_state
);
544 self.access_lvalue(context
, lvalue_span
, (kind
, Write(WriteKind
::Mutate
)), flow_state
);
546 // check for reassignments to immutable local variables
547 self.check_if_reassignment_to_immutable_state(context
, lvalue_span
, flow_state
);
550 fn consume_rvalue(&mut self,
552 (rvalue
, span
): (&Rvalue
<'tcx
>, Span
),
554 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>) {
556 Rvalue
::Ref(_
/*rgn*/, bk
, ref lvalue
) => {
557 let access_kind
= match bk
{
558 BorrowKind
::Shared
=> (Deep
, Read(ReadKind
::Borrow(bk
))),
560 BorrowKind
::Mut
=> (Deep
, Write(WriteKind
::MutableBorrow(bk
))),
562 self.access_lvalue(context
, (lvalue
, span
), access_kind
, flow_state
);
563 self.check_if_path_is_moved(context
, "borrow", (lvalue
, span
), flow_state
);
566 Rvalue
::Use(ref operand
) |
567 Rvalue
::Repeat(ref operand
, _
) |
568 Rvalue
::UnaryOp(_
/*un_op*/, ref operand
) |
569 Rvalue
::Cast(_
/*cast_kind*/, ref operand
, _
/*ty*/) => {
570 self.consume_operand(context
, Consume
, (operand
, span
), flow_state
)
573 Rvalue
::Len(ref lvalue
) |
574 Rvalue
::Discriminant(ref lvalue
) => {
575 let af
= match *rvalue
{
576 Rvalue
::Len(..) => ArtificialField
::ArrayLength
,
577 Rvalue
::Discriminant(..) => ArtificialField
::Discriminant
,
581 context
, (lvalue
, span
), (Shallow(Some(af
)), Read(ReadKind
::Copy
)), flow_state
);
582 self.check_if_path_is_moved(context
, "use", (lvalue
, span
), flow_state
);
585 Rvalue
::BinaryOp(_bin_op
, ref operand1
, ref operand2
) |
586 Rvalue
::CheckedBinaryOp(_bin_op
, ref operand1
, ref operand2
) => {
587 self.consume_operand(context
, Consume
, (operand1
, span
), flow_state
);
588 self.consume_operand(context
, Consume
, (operand2
, span
), flow_state
);
591 Rvalue
::NullaryOp(_op
, _ty
) => {
592 // nullary ops take no dynamic input; no borrowck effect.
594 // FIXME: is above actually true? Do we want to track
595 // the fact that uninitialized data can be created via
599 Rvalue
::Aggregate(ref _aggregate_kind
, ref operands
) => {
600 for operand
in operands
{
601 self.consume_operand(context
, Consume
, (operand
, span
), flow_state
);
607 fn consume_operand(&mut self,
609 consume_via_drop
: ConsumeKind
,
610 (operand
, span
): (&Operand
<'tcx
>, Span
),
611 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>) {
613 Operand
::Consume(ref lvalue
) => {
614 self.consume_lvalue(context
, consume_via_drop
, (lvalue
, span
), flow_state
)
616 Operand
::Constant(_
) => {}
620 fn consume_lvalue(&mut self,
622 consume_via_drop
: ConsumeKind
,
623 lvalue_span
: (&Lvalue
<'tcx
>, Span
),
624 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>) {
625 let lvalue
= lvalue_span
.0;
627 let ty
= lvalue
.ty(self.mir
, self.tcx
).to_ty(self.tcx
);
629 // Erase the regions in type before checking whether it moves by
630 // default. There are a few reasons to do this:
632 // - They should not affect the result.
633 // - It avoids adding new region constraints into the surrounding context,
634 // which would trigger an ICE, since the infcx will have been "frozen" by
635 // the NLL region context.
636 let gcx
= self.tcx
.global_tcx();
637 let erased_ty
= gcx
.lift(&self.tcx
.erase_regions(&ty
)).unwrap();
638 let moves_by_default
= erased_ty
.moves_by_default(gcx
, self.param_env
, DUMMY_SP
);
640 // Check if error has already been reported to stop duplicate reporting.
641 let has_storage_drop_or_dead_error_reported
= match *lvalue
{
642 Lvalue
::Local(local
) => self.storage_drop_or_dead_error_reported
.contains(&local
),
646 // If the error has been reported already, then we don't need the access_lvalue call.
647 if !has_storage_drop_or_dead_error_reported
|| consume_via_drop
!= ConsumeKind
::Drop
{
650 if moves_by_default
{
651 let kind
= match consume_via_drop
{
652 ConsumeKind
::Drop
=> WriteKind
::StorageDeadOrDrop
,
653 _
=> WriteKind
::Move
,
656 // move of lvalue: check if this is move of already borrowed path
657 error_reported
= self.access_lvalue(context
, lvalue_span
,
658 (Deep
, Write(kind
)), flow_state
);
660 // copy of lvalue: check if this is "copy of frozen path"
661 // (FIXME: see check_loans.rs)
662 error_reported
= self.access_lvalue(context
, lvalue_span
,
663 (Deep
, Read(ReadKind
::Copy
)), flow_state
);
666 // If there was an error, then we keep track of it so as to deduplicate it.
667 // We only do this on ConsumeKind::Drop.
668 if error_reported
&& consume_via_drop
== ConsumeKind
::Drop
{
669 if let Lvalue
::Local(local
) = *lvalue
{
670 self.storage_drop_or_dead_error_reported
.insert(local
);
675 // Finally, check if path was already moved.
676 match consume_via_drop
{
677 ConsumeKind
::Drop
=> {
678 // If path is merely being dropped, then we'll already
679 // check the drop flag to see if it is moved (thus we
680 // skip this check in that case).
682 ConsumeKind
::Consume
=> {
683 self.check_if_path_is_moved(context
, "use", lvalue_span
, flow_state
);
689 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
690 fn check_if_reassignment_to_immutable_state(&mut self,
692 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
),
693 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>) {
694 let move_data
= self.move_data
;
696 // determine if this path has a non-mut owner (and thus needs checking).
700 Lvalue
::Projection(ref proj
) => {
704 Lvalue
::Local(local
) => {
705 match self.mir
.local_decls
[local
].mutability
{
706 Mutability
::Not
=> break, // needs check
707 Mutability
::Mut
=> return,
710 Lvalue
::Static(ref static_
) => {
711 // mutation of non-mut static is always illegal,
712 // independent of dataflow.
713 if !self.tcx
.is_static_mut(static_
.def_id
) {
714 self.report_assignment_to_static(context
, (lvalue
, span
));
721 if let Some(mpi
) = self.move_path_for_lvalue(lvalue
) {
722 if flow_state
.inits
.curr_state
.contains(&mpi
) {
723 // may already be assigned before reaching this statement;
725 // FIXME: Not ideal, it only finds the assignment that lexically comes first
726 let assigned_lvalue
= &move_data
.move_paths
[mpi
].lvalue
;
727 let assignment_stmt
= self.mir
.basic_blocks().iter().filter_map(|bb
| {
728 bb
.statements
.iter().find(|stmt
| {
729 if let StatementKind
::Assign(ref lv
, _
) = stmt
.kind
{
730 *lv
== *assigned_lvalue
736 self.report_illegal_reassignment(
737 context
, (lvalue
, span
), assignment_stmt
.source_info
.span
);
742 fn check_if_path_is_moved(&mut self,
744 desired_action
: &str,
745 lvalue_span
: (&Lvalue
<'tcx
>, Span
),
746 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>) {
747 // FIXME: analogous code in check_loans first maps `lvalue` to
748 // its base_path ... but is that what we want here?
749 let lvalue
= self.base_path(lvalue_span
.0);
751 let maybe_uninits
= &flow_state
.uninits
;
752 let curr_move_outs
= &flow_state
.move_outs
.curr_state
;
756 // 1. Move of `a.b.c`, use of `a.b.c`
757 // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
758 // 3. Move of `a.b.c`, use of `a` or `a.b`
759 // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
760 // partial initialization support, one might have `a.x`
761 // initialized but not `a.b`.
765 // 5. Move of `a.b.c`, use of `a.b.d`
766 // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
767 // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
768 // must have been initialized for the use to be sound.
769 // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
771 // The dataflow tracks shallow prefixes distinctly (that is,
772 // field-accesses on P distinctly from P itself), in order to
773 // track substructure initialization separately from the whole
776 // E.g., when looking at (*a.b.c).d, if the closest prefix for
777 // which we have a MovePath is `a.b`, then that means that the
778 // initialization state of `a.b` is all we need to inspect to
779 // know if `a.b.c` is valid (and from that we infer that the
780 // dereference and `.d` access is also valid, since we assume
781 // `a.b.c` is assigned a reference to a initialized and
782 // well-formed record structure.)
784 // Therefore, if we seek out the *closest* prefix for which we
785 // have a MovePath, that should capture the initialization
786 // state for the lvalue scenario.
788 // This code covers scenarios 1, 2, and 4.
790 debug
!("check_if_path_is_moved part1 lvalue: {:?}", lvalue
);
791 match self.move_path_closest_to(lvalue
) {
793 if maybe_uninits
.curr_state
.contains(&mpi
) {
794 self.report_use_of_moved_or_uninitialized(context
, desired_action
,
797 return; // don't bother finding other problems.
800 Err(NoMovePathFound
::ReachedStatic
) => {
801 // Okay: we do not build MoveData for static variables
804 // Only query longest prefix with a MovePath, not further
805 // ancestors; dataflow recurs on children when parents
806 // move (to support partial (re)inits).
808 // (I.e. querying parents breaks scenario 8; but may want
809 // to do such a query based on partial-init feature-gate.)
812 // A move of any shallow suffix of `lvalue` also interferes
813 // with an attempt to use `lvalue`. This is scenario 3 above.
815 // (Distinct from handling of scenarios 1+2+4 above because
816 // `lvalue` does not interfere with suffixes of its prefixes,
817 // e.g. `a.b.c` does not interfere with `a.b.d`)
819 debug
!("check_if_path_is_moved part2 lvalue: {:?}", lvalue
);
820 if let Some(mpi
) = self.move_path_for_lvalue(lvalue
) {
821 if let Some(child_mpi
) = maybe_uninits
.has_any_child_of(mpi
) {
822 self.report_use_of_moved_or_uninitialized(context
, desired_action
,
823 lvalue_span
, child_mpi
,
825 return; // don't bother finding other problems.
830 /// Currently MoveData does not store entries for all lvalues in
831 /// the input MIR. For example it will currently filter out
832 /// lvalues that are Copy; thus we do not track lvalues of shared
833 /// reference type. This routine will walk up an lvalue along its
834 /// prefixes, searching for a foundational lvalue that *is*
835 /// tracked in the MoveData.
837 /// An Err result includes a tag indicated why the search failed.
838 /// Currenly this can only occur if the lvalue is built off of a
839 /// static variable, as we do not track those in the MoveData.
840 fn move_path_closest_to(&mut self, lvalue
: &Lvalue
<'tcx
>)
841 -> Result
<MovePathIndex
, NoMovePathFound
>
843 let mut last_prefix
= lvalue
;
844 for prefix
in self.prefixes(lvalue
, PrefixSet
::All
) {
845 if let Some(mpi
) = self.move_path_for_lvalue(prefix
) {
848 last_prefix
= prefix
;
851 Lvalue
::Local(_
) => panic
!("should have move path for every Local"),
852 Lvalue
::Projection(_
) => panic
!("PrefixSet::All meant dont stop for Projection"),
853 Lvalue
::Static(_
) => return Err(NoMovePathFound
::ReachedStatic
),
857 fn move_path_for_lvalue(&mut self,
858 lvalue
: &Lvalue
<'tcx
>)
859 -> Option
<MovePathIndex
>
861 // If returns None, then there is no move path corresponding
862 // to a direct owner of `lvalue` (which means there is nothing
863 // that borrowck tracks for its analysis).
865 match self.move_data
.rev_lookup
.find(lvalue
) {
866 LookupResult
::Parent(_
) => None
,
867 LookupResult
::Exact(mpi
) => Some(mpi
),
871 fn check_if_assigned_path_is_moved(&mut self,
873 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
),
874 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>) {
875 // recur down lvalue; dispatch to check_if_path_is_moved when necessary
876 let mut lvalue
= lvalue
;
879 Lvalue
::Local(_
) | Lvalue
::Static(_
) => {
880 // assigning to `x` does not require `x` be initialized.
883 Lvalue
::Projection(ref proj
) => {
884 let Projection { ref base, ref elem }
= **proj
;
886 ProjectionElem
::Deref
|
887 // assigning to *P requires `P` initialized.
888 ProjectionElem
::Index(_
/*operand*/) |
889 ProjectionElem
::ConstantIndex { .. }
|
890 // assigning to P[i] requires `P` initialized.
891 ProjectionElem
::Downcast(_
/*adt_def*/, _
/*variant_idx*/) =>
892 // assigning to (P->variant) is okay if assigning to `P` is okay
894 // FIXME: is this true even if P is a adt with a dtor?
897 ProjectionElem
::Subslice { .. }
=> {
898 panic
!("we dont allow assignments to subslices, context: {:?}",
902 ProjectionElem
::Field(..) => {
903 // if type of `P` has a dtor, then
904 // assigning to `P.f` requires `P` itself
905 // be already initialized
907 match base
.ty(self.mir
, tcx
).to_ty(tcx
).sty
{
908 ty
::TyAdt(def
, _
) if def
.has_dtor(tcx
) => {
910 // FIXME: analogous code in
911 // check_loans.rs first maps
912 // `base` to its base_path.
914 self.check_if_path_is_moved(
915 context
, "assignment", (base
, span
), flow_state
);
917 // (base initialized; no need to
933 /// Check the permissions for the given lvalue and read or write kind
934 fn check_access_permissions(&self, (lvalue
, span
): (&Lvalue
<'tcx
>, Span
), kind
: ReadOrWrite
) {
936 Write(WriteKind
::MutableBorrow(BorrowKind
::Unique
)) => {
937 if let Err(_lvalue_err
) = self.is_unique(lvalue
) {
938 span_bug
!(span
, "&unique borrow for `{}` should not fail",
939 self.describe_lvalue(lvalue
));
942 Write(WriteKind
::MutableBorrow(BorrowKind
::Mut
)) => {
943 if let Err(lvalue_err
) = self.is_mutable(lvalue
) {
944 let mut err
= self.tcx
.cannot_borrow_path_as_mutable(span
,
945 &format
!("immutable item `{}`",
946 self.describe_lvalue(lvalue
)),
948 err
.span_label(span
, "cannot borrow as mutable");
950 if lvalue
!= lvalue_err
{
951 err
.note(&format
!("Value not mutable causing this error: `{}`",
952 self.describe_lvalue(lvalue_err
)));
958 _
=> {}
// Access authorized
962 /// Can this value be written or borrowed mutably
963 fn is_mutable
<'d
>(&self, lvalue
: &'d Lvalue
<'tcx
>) -> Result
<(), &'d Lvalue
<'tcx
>> {
965 Lvalue
::Local(local
) => {
966 let local
= &self.mir
.local_decls
[local
];
967 match local
.mutability
{
968 Mutability
::Not
=> Err(lvalue
),
969 Mutability
::Mut
=> Ok(())
972 Lvalue
::Static(ref static_
) => {
973 if !self.tcx
.is_static_mut(static_
.def_id
) {
979 Lvalue
::Projection(ref proj
) => {
981 ProjectionElem
::Deref
=> {
982 let base_ty
= proj
.base
.ty(self.mir
, self.tcx
).to_ty(self.tcx
);
984 // `Box<T>` owns its content, so mutable if its location is mutable
985 if base_ty
.is_box() {
986 return self.is_mutable(&proj
.base
);
989 // Otherwise we check the kind of deref to decide
991 ty
::TyRef(_
, tnm
) => {
993 // Shared borrowed data is never mutable
994 hir
::MutImmutable
=> Err(lvalue
),
995 // Mutably borrowed data is mutable, but only if we have a
996 // unique path to the `&mut`
997 hir
::MutMutable
=> self.is_unique(&proj
.base
),
1000 ty
::TyRawPtr(tnm
) => {
1002 // `*const` raw pointers are not mutable
1003 hir
::MutImmutable
=> Err(lvalue
),
1004 // `*mut` raw pointers are always mutable, regardless of context
1005 // The users have to check by themselve.
1006 hir
::MutMutable
=> Ok(()),
1009 // Deref should only be for reference, pointers or boxes
1010 _
=> bug
!("Deref of unexpected type: {:?}", base_ty
)
1013 // All other projections are owned by their base path, so mutable if
1014 // base path is mutable
1015 ProjectionElem
::Field(..) |
1016 ProjectionElem
::Index(..) |
1017 ProjectionElem
::ConstantIndex{..}
|
1018 ProjectionElem
::Subslice{..}
|
1019 ProjectionElem
::Downcast(..) =>
1020 self.is_mutable(&proj
.base
)
1026 /// Does this lvalue have a unique path
1027 fn is_unique
<'d
>(&self, lvalue
: &'d Lvalue
<'tcx
>) -> Result
<(), &'d Lvalue
<'tcx
>> {
1029 Lvalue
::Local(..) => {
1030 // Local variables are unique
1033 Lvalue
::Static(..) => {
1034 // Static variables are not
1037 Lvalue
::Projection(ref proj
) => {
1039 ProjectionElem
::Deref
=> {
1040 let base_ty
= proj
.base
.ty(self.mir
, self.tcx
).to_ty(self.tcx
);
1042 // `Box<T>` referent is unique if box is a unique spot
1043 if base_ty
.is_box() {
1044 return self.is_unique(&proj
.base
);
1047 // Otherwise we check the kind of deref to decide
1049 ty
::TyRef(_
, tnm
) => {
1051 // lvalue represent an aliased location
1052 hir
::MutImmutable
=> Err(lvalue
),
1053 // `&mut T` is as unique as the context in which it is found
1054 hir
::MutMutable
=> self.is_unique(&proj
.base
),
1057 ty
::TyRawPtr(tnm
) => {
1059 // `*mut` can be aliased, but we leave it to user
1060 hir
::MutMutable
=> Ok(()),
1061 // `*const` is treated the same as `*mut`
1062 hir
::MutImmutable
=> Ok(()),
1065 // Deref should only be for reference, pointers or boxes
1066 _
=> bug
!("Deref of unexpected type: {:?}", base_ty
)
1069 // Other projections are unique if the base is unique
1070 ProjectionElem
::Field(..) |
1071 ProjectionElem
::Index(..) |
1072 ProjectionElem
::ConstantIndex{..}
|
1073 ProjectionElem
::Subslice{..}
|
1074 ProjectionElem
::Downcast(..) =>
1075 self.is_unique(&proj
.base
)
1082 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1083 enum NoMovePathFound
{
1087 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
1088 fn each_borrow_involving_path
<F
>(&mut self,
1090 access_lvalue
: (ShallowOrDeep
, &Lvalue
<'tcx
>),
1091 flow_state
: &InProgress
<'cx
, 'gcx
, 'tcx
>,
1093 where F
: FnMut(&mut Self, BorrowIndex
, &BorrowData
<'tcx
>, &Lvalue
<'tcx
>) -> Control
1095 let (access
, lvalue
) = access_lvalue
;
1097 // FIXME: analogous code in check_loans first maps `lvalue` to
1100 let domain
= flow_state
.borrows
.base_results
.operator();
1101 let data
= domain
.borrows();
1103 // check for loan restricting path P being used. Accounts for
1104 // borrows of P, P.a.b, etc.
1105 'next_borrow
: for i
in flow_state
.borrows
.elems_incoming() {
1106 let borrowed
= &data
[i
];
1108 // Is `lvalue` (or a prefix of it) already borrowed? If
1109 // so, that's relevant.
1111 // FIXME: Differs from AST-borrowck; includes drive-by fix
1112 // to #38899. Will probably need back-compat mode flag.
1113 for accessed_prefix
in self.prefixes(lvalue
, PrefixSet
::All
) {
1114 if *accessed_prefix
== borrowed
.lvalue
{
1115 // FIXME: pass in enum describing case we are in?
1116 let ctrl
= op(self, i
, borrowed
, accessed_prefix
);
1117 if ctrl
== Control
::Break { return; }
1121 // Is `lvalue` a prefix (modulo access type) of the
1122 // `borrowed.lvalue`? If so, that's relevant.
1124 let prefix_kind
= match access
{
1125 Shallow(Some(ArtificialField
::Discriminant
)) |
1126 Shallow(Some(ArtificialField
::ArrayLength
)) => {
1127 // The discriminant and array length are like
1128 // additional fields on the type; they do not
1129 // overlap any existing data there. Furthermore,
1130 // they cannot actually be a prefix of any
1131 // borrowed lvalue (at least in MIR as it is
1133 continue 'next_borrow
;
1135 Shallow(None
) => PrefixSet
::Shallow
,
1136 Deep
=> PrefixSet
::Supporting
,
1139 for borrowed_prefix
in self.prefixes(&borrowed
.lvalue
, prefix_kind
) {
1140 if borrowed_prefix
== lvalue
{
1141 // FIXME: pass in enum describing case we are in?
1142 let ctrl
= op(self, i
, borrowed
, borrowed_prefix
);
1143 if ctrl
== Control
::Break { return; }
1150 use self::prefixes
::PrefixSet
;
1152 /// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an
1153 /// lvalue are formed by stripping away fields and derefs, except that
1154 /// we stop when we reach the deref of a shared reference. [...] "
1156 /// "Shallow prefixes are found by stripping away fields, but stop at
1157 /// any dereference. So: writing a path like `a` is illegal if `a.b`
1158 /// is borrowed. But: writing `a` is legal if `*a` is borrowed,
1159 /// whether or not `a` is a shared or mutable reference. [...] "
1161 use super::{MirBorrowckCtxt}
;
1164 use rustc
::ty
::{self, TyCtxt}
;
1165 use rustc
::mir
::{Lvalue, Mir, ProjectionElem}
;
1167 pub trait IsPrefixOf
<'tcx
> {
1168 fn is_prefix_of(&self, other
: &Lvalue
<'tcx
>) -> bool
;
1171 impl<'tcx
> IsPrefixOf
<'tcx
> for Lvalue
<'tcx
> {
1172 fn is_prefix_of(&self, other
: &Lvalue
<'tcx
>) -> bool
{
1173 let mut cursor
= other
;
1181 Lvalue
::Static(_
) => return false,
1182 Lvalue
::Projection(ref proj
) => {
1183 cursor
= &proj
.base
;
1191 pub(super) struct Prefixes
<'cx
, 'gcx
: 'tcx
, 'tcx
: 'cx
> {
1192 mir
: &'cx Mir
<'tcx
>,
1193 tcx
: TyCtxt
<'cx
, 'gcx
, 'tcx
>,
1195 next
: Option
<&'cx Lvalue
<'tcx
>>,
1198 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1199 pub(super) enum PrefixSet
{
1200 /// Doesn't stop until it returns the base case (a Local or
1203 /// Stops at any dereference.
1205 /// Stops at the deref of a shared reference.
1209 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
1210 /// Returns an iterator over the prefixes of `lvalue`
1211 /// (inclusive) from longest to smallest, potentially
1212 /// terminating the iteration early based on `kind`.
1213 pub(super) fn prefixes(&self,
1214 lvalue
: &'cx Lvalue
<'tcx
>,
1216 -> Prefixes
<'cx
, 'gcx
, 'tcx
>
1218 Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx }
1222 impl<'cx
, 'gcx
, 'tcx
> Iterator
for Prefixes
<'cx
, 'gcx
, 'tcx
> {
1223 type Item
= &'cx Lvalue
<'tcx
>;
1224 fn next(&mut self) -> Option
<Self::Item
> {
1225 let mut cursor
= match self.next
{
1226 None
=> return None
,
1227 Some(lvalue
) => lvalue
,
1230 // Post-processing `lvalue`: Enqueue any remaining
1231 // work. Also, `lvalue` may not be a prefix itself, but
1232 // may hold one further down (e.g. we never return
1233 // downcasts here, but may return a base of a downcast).
1236 let proj
= match *cursor
{
1237 Lvalue
::Local(_
) | // search yielded this leaf
1238 Lvalue
::Static(_
) => {
1240 return Some(cursor
);
1243 Lvalue
::Projection(ref proj
) => proj
,
1247 ProjectionElem
::Field(_
/*field*/, _
/*ty*/) => {
1248 // FIXME: add union handling
1249 self.next
= Some(&proj
.base
);
1250 return Some(cursor
);
1252 ProjectionElem
::Downcast(..) |
1253 ProjectionElem
::Subslice { .. }
|
1254 ProjectionElem
::ConstantIndex { .. }
|
1255 ProjectionElem
::Index(_
) => {
1256 cursor
= &proj
.base
;
1259 ProjectionElem
::Deref
=> {
1264 assert_eq
!(proj
.elem
, ProjectionElem
::Deref
);
1267 PrefixSet
::Shallow
=> {
1268 // shallow prefixes are found by stripping away
1269 // fields, but stop at *any* dereference.
1270 // So we can just stop the traversal now.
1272 return Some(cursor
);
1275 // all prefixes: just blindly enqueue the base
1276 // of the projection
1277 self.next
= Some(&proj
.base
);
1278 return Some(cursor
);
1280 PrefixSet
::Supporting
=> {
1285 assert_eq
!(self.kind
, PrefixSet
::Supporting
);
1286 // supporting prefixes: strip away fields and
1287 // derefs, except we stop at the deref of a shared
1290 let ty
= proj
.base
.ty(self.mir
, self.tcx
).to_ty(self.tcx
);
1293 ty
::TyRef(_
/*rgn*/, ty
::TypeAndMut { ty: _, mutbl: hir::MutImmutable }
) => {
1294 // don't continue traversing over derefs of raw pointers or shared borrows.
1296 return Some(cursor
);
1299 ty
::TyRef(_
/*rgn*/, ty
::TypeAndMut { ty: _, mutbl: hir::MutMutable }
) => {
1300 self.next
= Some(&proj
.base
);
1301 return Some(cursor
);
1304 ty
::TyAdt(..) if ty
.is_box() => {
1305 self.next
= Some(&proj
.base
);
1306 return Some(cursor
);
1309 _
=> panic
!("unknown type fed to Projection Deref."),
1316 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
1317 fn report_use_of_moved_or_uninitialized(&mut self,
1319 desired_action
: &str,
1320 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
),
1322 curr_move_out
: &IdxSetBuf
<MoveOutIndex
>) {
1324 let mois
= self.move_data
.path_map
[mpi
].iter().filter(
1325 |moi
| curr_move_out
.contains(moi
)).collect
::<Vec
<_
>>();
1327 if mois
.is_empty() {
1328 self.tcx
.cannot_act_on_uninitialized_variable(span
,
1330 &self.describe_lvalue(lvalue
),
1332 .span_label(span
, format
!("use of possibly uninitialized `{}`",
1333 self.describe_lvalue(lvalue
)))
1336 let msg
= ""; //FIXME: add "partially " or "collaterally "
1338 let mut err
= self.tcx
.cannot_act_on_moved_value(span
,
1341 &self.describe_lvalue(lvalue
),
1343 err
.span_label(span
, format
!("value {} here after move", desired_action
));
1345 let move_msg
= ""; //FIXME: add " (into closure)"
1346 let move_span
= self.mir
.source_info(self.move_data
.moves
[*moi
].source
).span
;
1347 if span
== move_span
{
1348 err
.span_label(span
,
1349 format
!("value moved{} here in previous iteration of loop",
1352 err
.span_label(move_span
, format
!("value moved{} here", move_msg
));
1355 //FIXME: add note for closure
1360 fn report_move_out_while_borrowed(&mut self,
1362 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
),
1363 borrow
: &BorrowData
<'tcx
>) {
1364 self.tcx
.cannot_move_when_borrowed(span
,
1365 &self.describe_lvalue(lvalue
),
1367 .span_label(self.retrieve_borrow_span(borrow
),
1368 format
!("borrow of `{}` occurs here",
1369 self.describe_lvalue(&borrow
.lvalue
)))
1370 .span_label(span
, format
!("move out of `{}` occurs here",
1371 self.describe_lvalue(lvalue
)))
1375 fn report_use_while_mutably_borrowed(&mut self,
1377 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
),
1378 borrow
: &BorrowData
<'tcx
>) {
1380 let mut err
= self.tcx
.cannot_use_when_mutably_borrowed(
1381 span
, &self.describe_lvalue(lvalue
),
1382 self.retrieve_borrow_span(borrow
), &self.describe_lvalue(&borrow
.lvalue
),
1388 /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
1389 /// the local assigned at `location`.
1390 /// This is done by searching in statements succeeding `location`
1391 /// and originating from `maybe_closure_span`.
1392 fn find_closure_span(
1394 maybe_closure_span
: Span
,
1396 ) -> Option
<(Span
, Span
)> {
1397 use rustc
::hir
::ExprClosure
;
1398 use rustc
::mir
::AggregateKind
;
1400 let local
= if let StatementKind
::Assign(Lvalue
::Local(local
), _
) =
1401 self.mir
[location
.block
].statements
[location
.statement_index
].kind
1408 for stmt
in &self.mir
[location
.block
].statements
[location
.statement_index
+ 1..] {
1409 if maybe_closure_span
!= stmt
.source_info
.span
{
1413 if let StatementKind
::Assign(_
, Rvalue
::Aggregate(ref kind
, ref lvs
)) = stmt
.kind
{
1414 if let AggregateKind
::Closure(def_id
, _
) = **kind
{
1415 debug
!("find_closure_span: found closure {:?}", lvs
);
1417 return if let Some(node_id
) = self.tcx
.hir
.as_local_node_id(def_id
) {
1418 let args_span
= if let ExprClosure(_
, _
, _
, span
, _
) =
1419 self.tcx
.hir
.expect_expr(node_id
).node
1427 .with_freevars(node_id
, |freevars
| {
1428 for (v
, lv
) in freevars
.iter().zip(lvs
) {
1429 if let Operand
::Consume(Lvalue
::Local(l
)) = *lv
{
1432 "find_closure_span: found captured local {:?}",
1435 return Some(v
.span
);
1441 .map(|var_span
| (args_span
, var_span
))
1452 fn report_conflicting_borrow(&mut self,
1454 common_prefix
: &Lvalue
<'tcx
>,
1455 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
),
1456 gen_borrow_kind
: BorrowKind
,
1457 issued_borrow
: &BorrowData
,
1458 end_issued_loan_span
: Option
<Span
>) {
1459 use self::prefixes
::IsPrefixOf
;
1461 assert
!(common_prefix
.is_prefix_of(lvalue
));
1462 assert
!(common_prefix
.is_prefix_of(&issued_borrow
.lvalue
));
1464 let issued_span
= self.retrieve_borrow_span(issued_borrow
);
1466 let new_closure_span
= self.find_closure_span(span
, context
.loc
);
1467 let span
= new_closure_span
.map(|(args
, _
)| args
).unwrap_or(span
);
1468 let old_closure_span
= self.find_closure_span(issued_span
, issued_borrow
.location
);
1469 let issued_span
= old_closure_span
.map(|(args
, _
)| args
).unwrap_or(issued_span
);
1471 let desc_lvalue
= self.describe_lvalue(lvalue
);
1473 // FIXME: supply non-"" `opt_via` when appropriate
1474 let mut err
= match (gen_borrow_kind
, "immutable", "mutable",
1475 issued_borrow
.kind
, "immutable", "mutable") {
1476 (BorrowKind
::Shared
, lft
, _
, BorrowKind
::Mut
, _
, rgt
) |
1477 (BorrowKind
::Mut
, _
, lft
, BorrowKind
::Shared
, rgt
, _
) =>
1478 self.tcx
.cannot_reborrow_already_borrowed(
1479 span
, &desc_lvalue
, "", lft
, issued_span
,
1480 "it", rgt
, "", end_issued_loan_span
, Origin
::Mir
),
1482 (BorrowKind
::Mut
, _
, _
, BorrowKind
::Mut
, _
, _
) =>
1483 self.tcx
.cannot_mutably_borrow_multiply(
1484 span
, &desc_lvalue
, "", issued_span
,
1485 "", end_issued_loan_span
, Origin
::Mir
),
1487 (BorrowKind
::Unique
, _
, _
, BorrowKind
::Unique
, _
, _
) =>
1488 self.tcx
.cannot_uniquely_borrow_by_two_closures(
1489 span
, &desc_lvalue
, issued_span
,
1490 end_issued_loan_span
, Origin
::Mir
),
1492 (BorrowKind
::Unique
, _
, _
, _
, _
, _
) =>
1493 self.tcx
.cannot_uniquely_borrow_by_one_closure(
1494 span
, &desc_lvalue
, "",
1495 issued_span
, "it", "", end_issued_loan_span
, Origin
::Mir
),
1497 (_
, _
, _
, BorrowKind
::Unique
, _
, _
) =>
1498 self.tcx
.cannot_reborrow_already_uniquely_borrowed(
1499 span
, &desc_lvalue
, "it", "",
1500 issued_span
, "", end_issued_loan_span
, Origin
::Mir
),
1502 (BorrowKind
::Shared
, _
, _
, BorrowKind
::Shared
, _
, _
) =>
1506 if let Some((_
, var_span
)) = old_closure_span
{
1509 format
!("previous borrow occurs due to use of `{}` in closure", desc_lvalue
),
1513 if let Some((_
, var_span
)) = new_closure_span
{
1516 format
!("borrow occurs due to use of `{}` in closure", desc_lvalue
),
1523 fn report_borrowed_value_does_not_live_long_enough(&mut self,
1525 (lvalue
, span
): (&Lvalue
, Span
),
1526 end_span
: Option
<Span
>) {
1527 let proper_span
= match *lvalue
{
1528 Lvalue
::Local(local
) => self.mir
.local_decls
[local
].source_info
.span
,
1532 let mut err
= self.tcx
.path_does_not_live_long_enough(span
, "borrowed value", Origin
::Mir
);
1533 err
.span_label(proper_span
, "temporary value created here");
1534 err
.span_label(span
, "temporary value dropped here while still borrowed");
1535 err
.note("consider using a `let` binding to increase its lifetime");
1537 if let Some(end
) = end_span
{
1538 err
.span_label(end
, "temporary value needs to live until here");
1544 fn report_illegal_mutation_of_borrowed(&mut self,
1546 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
),
1547 loan
: &BorrowData
) {
1548 let mut err
= self.tcx
.cannot_assign_to_borrowed(
1549 span
, self.retrieve_borrow_span(loan
), &self.describe_lvalue(lvalue
), Origin
::Mir
);
1554 fn report_illegal_reassignment(&mut self,
1556 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
),
1557 assigned_span
: Span
) {
1558 self.tcx
.cannot_reassign_immutable(span
,
1559 &self.describe_lvalue(lvalue
),
1561 .span_label(span
, "cannot assign twice to immutable variable")
1562 .span_label(assigned_span
, format
!("first assignment to `{}`",
1563 self.describe_lvalue(lvalue
)))
1567 fn report_assignment_to_static(&mut self,
1569 (lvalue
, span
): (&Lvalue
<'tcx
>, Span
)) {
1570 let mut err
= self.tcx
.cannot_assign_static(
1571 span
, &self.describe_lvalue(lvalue
), Origin
::Mir
);
1576 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
1577 // End-user visible description of `lvalue`
1578 fn describe_lvalue(&self, lvalue
: &Lvalue
<'tcx
>) -> String
{
1579 let mut buf
= String
::new();
1580 self.append_lvalue_to_string(lvalue
, &mut buf
, None
);
1584 // Appends end-user visible description of `lvalue` to `buf`.
1585 fn append_lvalue_to_string(&self,
1586 lvalue
: &Lvalue
<'tcx
>,
1588 autoderef
: Option
<bool
>) {
1590 Lvalue
::Local(local
) => {
1591 self.append_local_to_string(local
, buf
, "_");
1593 Lvalue
::Static(ref static_
) => {
1594 buf
.push_str(&format
!("{}", &self.tcx
.item_name(static_
.def_id
)));
1596 Lvalue
::Projection(ref proj
) => {
1597 let mut autoderef
= autoderef
.unwrap_or(false);
1600 ProjectionElem
::Deref
=> {
1602 self.append_lvalue_to_string(&proj
.base
, buf
, Some(autoderef
));
1604 buf
.push_str(&"(*");
1605 self.append_lvalue_to_string(&proj
.base
, buf
, Some(autoderef
));
1609 ProjectionElem
::Downcast(..) => {
1610 self.append_lvalue_to_string(&proj
.base
, buf
, Some(autoderef
));
1612 ProjectionElem
::Field(field
, _ty
) => {
1614 let is_projection_from_ty_closure
= proj
.base
.ty(self.mir
, self.tcx
)
1615 .to_ty(self.tcx
).is_closure();
1617 let field_name
= self.describe_field(&proj
.base
, field
.index());
1618 if is_projection_from_ty_closure
{
1619 buf
.push_str(&format
!("{}", field_name
));
1621 self.append_lvalue_to_string(&proj
.base
, buf
, Some(autoderef
));
1622 buf
.push_str(&format
!(".{}", field_name
));
1625 ProjectionElem
::Index(index
) => {
1628 self.append_lvalue_to_string(&proj
.base
, buf
, Some(autoderef
));
1630 self.append_local_to_string(index
, buf
, "..");
1633 ProjectionElem
::ConstantIndex { .. }
| ProjectionElem
::Subslice { .. }
=> {
1635 // Since it isn't possible to borrow an element on a particular index and
1636 // then use another while the borrow is held, don't output indices details
1637 // to avoid confusing the end-user
1638 self.append_lvalue_to_string(&proj
.base
, buf
, Some(autoderef
));
1639 buf
.push_str(&"[..]");
1646 // Appends end-user visible description of the `local` lvalue to `buf`. If `local` doesn't have
1647 // a name, then `none_string` is appended instead
1648 fn append_local_to_string(&self, local_index
: Local
, buf
: &mut String
, none_string
: &str) {
1649 let local
= &self.mir
.local_decls
[local_index
];
1651 Some(name
) => buf
.push_str(&format
!("{}", name
)),
1652 None
=> buf
.push_str(none_string
)
1656 // FIXME Instead of passing usize, Field should be passed
1657 // End-user visible description of the `field_index`nth field of `base`
1658 fn describe_field(&self, base
: &Lvalue
, field_index
: usize) -> String
{
1660 Lvalue
::Local(local
) => {
1661 let local
= &self.mir
.local_decls
[local
];
1662 self.describe_field_from_ty(&local
.ty
, field_index
)
1664 Lvalue
::Static(ref static_
) => {
1665 self.describe_field_from_ty(&static_
.ty
, field_index
)
1667 Lvalue
::Projection(ref proj
) => {
1669 ProjectionElem
::Deref
=>
1670 self.describe_field(&proj
.base
, field_index
),
1671 ProjectionElem
::Downcast(def
, variant_index
) =>
1672 format
!("{}", def
.variants
[variant_index
].fields
[field_index
].name
),
1673 ProjectionElem
::Field(_
, field_type
) =>
1674 self.describe_field_from_ty(&field_type
, field_index
),
1675 ProjectionElem
::Index(..)
1676 | ProjectionElem
::ConstantIndex { .. }
1677 | ProjectionElem
::Subslice { .. }
=>
1678 format
!("{}", self.describe_field(&proj
.base
, field_index
)),
1684 // End-user visible description of the `field_index`nth field of `ty`
1685 fn describe_field_from_ty(&self, ty
: &ty
::Ty
, field_index
: usize) -> String
{
1687 // If the type is a box, the field is described from the boxed type
1688 self.describe_field_from_ty(&ty
.boxed_ty(), field_index
)
1692 ty
::TyAdt(def
, _
) => {
1694 format
!("{}", field_index
)
1697 format
!("{}", def
.struct_variant().fields
[field_index
].name
)
1700 ty
::TyTuple(_
, _
) => {
1701 format
!("{}", field_index
)
1703 ty
::TyRef(_
, tnm
) | ty
::TyRawPtr(tnm
) => {
1704 self.describe_field_from_ty(&tnm
.ty
, field_index
)
1706 ty
::TyArray(ty
, _
) | ty
::TySlice(ty
) => {
1707 self.describe_field_from_ty(&ty
, field_index
)
1709 ty
::TyClosure(closure_def_id
, _
) => {
1710 // Convert the def-id into a node-id. node-ids are only valid for
1711 // the local code in the current crate, so this returns an `Option` in case
1712 // the closure comes from another crate. But in that case we wouldn't
1713 // be borrowck'ing it, so we can just unwrap:
1714 let node_id
= self.tcx
.hir
.as_local_node_id(closure_def_id
).unwrap();
1715 let freevar
= self.tcx
.with_freevars(node_id
, |fv
| fv
[field_index
]);
1717 self.tcx
.hir
.name(freevar
.var_id()).to_string()
1720 // Might need a revision when the fields in trait RFC is implemented
1721 // (https://github.com/rust-lang/rfcs/pull/1546)
1722 bug
!("End-user description not implemented for field access on `{:?}`", ty
.sty
);
1728 // Retrieve span of given borrow from the current MIR representation
1729 fn retrieve_borrow_span(&self, borrow
: &BorrowData
) -> Span
{
1730 self.mir
.source_info(borrow
.location
).span
1734 impl<'cx
, 'gcx
, 'tcx
> MirBorrowckCtxt
<'cx
, 'gcx
, 'tcx
> {
1735 // FIXME (#16118): function intended to allow the borrow checker
1736 // to be less precise in its handling of Box while still allowing
1737 // moves out of a Box. They should be removed when/if we stop
1738 // treating Box specially (e.g. when/if DerefMove is added...)
1740 fn base_path
<'d
>(&self, lvalue
: &'d Lvalue
<'tcx
>) -> &'d Lvalue
<'tcx
> {
1741 //! Returns the base of the leftmost (deepest) dereference of an
1742 //! Box in `lvalue`. If there is no dereference of an Box
1743 //! in `lvalue`, then it just returns `lvalue` itself.
1745 let mut cursor
= lvalue
;
1746 let mut deepest
= lvalue
;
1748 let proj
= match *cursor
{
1749 Lvalue
::Local(..) | Lvalue
::Static(..) => return deepest
,
1750 Lvalue
::Projection(ref proj
) => proj
,
1752 if proj
.elem
== ProjectionElem
::Deref
&&
1753 lvalue
.ty(self.mir
, self.tcx
).to_ty(self.tcx
).is_box()
1755 deepest
= &proj
.base
;
1757 cursor
= &proj
.base
;
1762 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1768 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1786 fn new(self, loc
: Location
) -> Context { Context { kind: self, loc: loc }
}
1789 impl<'b
, 'gcx
, 'tcx
> InProgress
<'b
, 'gcx
, 'tcx
> {
1790 pub(super) fn new(borrows
: DataflowResults
<Borrows
<'b
, 'gcx
, 'tcx
>>,
1791 inits
: DataflowResults
<MaybeInitializedLvals
<'b
, 'gcx
, 'tcx
>>,
1792 uninits
: DataflowResults
<MaybeUninitializedLvals
<'b
, 'gcx
, 'tcx
>>,
1793 move_out
: DataflowResults
<MovingOutStatements
<'b
, 'gcx
, 'tcx
>>)
1796 borrows
: FlowInProgress
::new(borrows
),
1797 inits
: FlowInProgress
::new(inits
),
1798 uninits
: FlowInProgress
::new(uninits
),
1799 move_outs
: FlowInProgress
::new(move_out
)
1803 fn each_flow
<XB
, XI
, XU
, XM
>(&mut self,
1804 mut xform_borrows
: XB
,
1805 mut xform_inits
: XI
,
1806 mut xform_uninits
: XU
,
1807 mut xform_move_outs
: XM
) where
1808 XB
: FnMut(&mut FlowInProgress
<Borrows
<'b
, 'gcx
, 'tcx
>>),
1809 XI
: FnMut(&mut FlowInProgress
<MaybeInitializedLvals
<'b
, 'gcx
, 'tcx
>>),
1810 XU
: FnMut(&mut FlowInProgress
<MaybeUninitializedLvals
<'b
, 'gcx
, 'tcx
>>),
1811 XM
: FnMut(&mut FlowInProgress
<MovingOutStatements
<'b
, 'gcx
, 'tcx
>>),
1813 xform_borrows(&mut self.borrows
);
1814 xform_inits(&mut self.inits
);
1815 xform_uninits(&mut self.uninits
);
1816 xform_move_outs(&mut self.move_outs
);
1819 fn summary(&self) -> String
{
1820 let mut s
= String
::new();
1822 s
.push_str("borrows in effect: [");
1823 let mut saw_one
= false;
1824 self.borrows
.each_state_bit(|borrow
| {
1825 if saw_one { s.push_str(", "); }
;
1827 let borrow_data
= &self.borrows
.base_results
.operator().borrows()[borrow
];
1828 s
.push_str(&format
!("{}", borrow_data
));
1832 s
.push_str("borrows generated: [");
1833 let mut saw_one
= false;
1834 self.borrows
.each_gen_bit(|borrow
| {
1835 if saw_one { s.push_str(", "); }
;
1837 let borrow_data
= &self.borrows
.base_results
.operator().borrows()[borrow
];
1838 s
.push_str(&format
!("{}", borrow_data
));
1842 s
.push_str("inits: [");
1843 let mut saw_one
= false;
1844 self.inits
.each_state_bit(|mpi_init
| {
1845 if saw_one { s.push_str(", "); }
;
1848 &self.inits
.base_results
.operator().move_data().move_paths
[mpi_init
];
1849 s
.push_str(&format
!("{}", move_path
));
1853 s
.push_str("uninits: [");
1854 let mut saw_one
= false;
1855 self.uninits
.each_state_bit(|mpi_uninit
| {
1856 if saw_one { s.push_str(", "); }
;
1859 &self.uninits
.base_results
.operator().move_data().move_paths
[mpi_uninit
];
1860 s
.push_str(&format
!("{}", move_path
));
1864 s
.push_str("move_out: [");
1865 let mut saw_one
= false;
1866 self.move_outs
.each_state_bit(|mpi_move_out
| {
1867 if saw_one { s.push_str(", "); }
;
1870 &self.move_outs
.base_results
.operator().move_data().moves
[mpi_move_out
];
1871 s
.push_str(&format
!("{:?}", move_out
));
1879 impl<'b
, 'gcx
, 'tcx
> FlowInProgress
<MaybeUninitializedLvals
<'b
, 'gcx
, 'tcx
>> {
1880 fn has_any_child_of(&self, mpi
: MovePathIndex
) -> Option
<MovePathIndex
> {
1881 let move_data
= self.base_results
.operator().move_data();
1883 let mut todo
= vec
![mpi
];
1884 let mut push_siblings
= false; // don't look at siblings of original `mpi`.
1885 while let Some(mpi
) = todo
.pop() {
1886 if self.curr_state
.contains(&mpi
) {
1889 let move_path
= &move_data
.move_paths
[mpi
];
1890 if let Some(child
) = move_path
.first_child
{
1894 if let Some(sibling
) = move_path
.next_sibling
{
1898 // after we've processed the original `mpi`, we should
1899 // always traverse the siblings of any of its
1901 push_siblings
= true;
1908 impl<BD
> FlowInProgress
<BD
> where BD
: BitDenotation
{
1909 fn each_state_bit
<F
>(&self, f
: F
) where F
: FnMut(BD
::Idx
) {
1910 self.curr_state
.each_bit(self.base_results
.operator().bits_per_block(), f
)
1913 fn each_gen_bit
<F
>(&self, f
: F
) where F
: FnMut(BD
::Idx
) {
1914 self.stmt_gen
.each_bit(self.base_results
.operator().bits_per_block(), f
)
1917 fn new(results
: DataflowResults
<BD
>) -> Self {
1918 let bits_per_block
= results
.sets().bits_per_block();
1919 let curr_state
= IdxSetBuf
::new_empty(bits_per_block
);
1920 let stmt_gen
= IdxSetBuf
::new_empty(bits_per_block
);
1921 let stmt_kill
= IdxSetBuf
::new_empty(bits_per_block
);
1923 base_results
: results
,
1924 curr_state
: curr_state
,
1926 stmt_kill
: stmt_kill
,
1930 fn reset_to_entry_of(&mut self, bb
: BasicBlock
) {
1931 (*self.curr_state
).clone_from(self.base_results
.sets().on_entry_set_for(bb
.index()));
1934 fn reconstruct_statement_effect(&mut self, loc
: Location
) {
1935 self.stmt_gen
.reset_to_empty();
1936 self.stmt_kill
.reset_to_empty();
1937 let mut ignored
= IdxSetBuf
::new_empty(0);
1938 let mut sets
= BlockSets
{
1939 on_entry
: &mut ignored
, gen_set
: &mut self.stmt_gen
, kill_set
: &mut self.stmt_kill
,
1941 self.base_results
.operator().statement_effect(&mut sets
, loc
);
1944 fn reconstruct_terminator_effect(&mut self, loc
: Location
) {
1945 self.stmt_gen
.reset_to_empty();
1946 self.stmt_kill
.reset_to_empty();
1947 let mut ignored
= IdxSetBuf
::new_empty(0);
1948 let mut sets
= BlockSets
{
1949 on_entry
: &mut ignored
, gen_set
: &mut self.stmt_gen
, kill_set
: &mut self.stmt_kill
,
1951 self.base_results
.operator().terminator_effect(&mut sets
, loc
);
1954 fn apply_local_effect(&mut self) {
1955 self.curr_state
.union(&self.stmt_gen
);
1956 self.curr_state
.subtract(&self.stmt_kill
);
1959 fn elems_incoming(&self) -> indexed_set
::Elems
<BD
::Idx
> {
1960 let univ
= self.base_results
.sets().bits_per_block();
1961 self.curr_state
.elems(univ
)