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::{Field, 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};
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, EverInitializedLvals};
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};
43 pub fn provide(providers: &mut Providers) {
44 *providers = Providers {
50 fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
51 let input_mir = tcx.mir_validated(def_id);
52 debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
55 !tcx.has_attr(def_id, "rustc_mir_borrowck") &&
56 !tcx.sess.opts.borrowck_mode.use_mir() &&
57 !tcx.sess.opts.debugging_opts.nll
62 tcx.infer_ctxt().enter(|infcx| {
63 let input_mir: &Mir = &input_mir.borrow();
64 do_mir_borrowck(&infcx, input_mir, def_id);
66 debug!("mir_borrowck done");
69 fn do_mir_borrowck<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
70 input_mir: &Mir<'gcx>,
74 let attributes = tcx.get_attrs(def_id);
75 let param_env = tcx.param_env(def_id);
76 let id = tcx.hir.as_local_node_id(def_id)
77 .expect("do_mir_borrowck: non-local DefId");
79 let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx) {
80 Ok(move_data) => move_data,
81 Err((move_data, move_errors)) => {
82 for move_error in move_errors {
83 let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
84 MoveError::UnionMove { .. } =>
85 unimplemented!("dont know how to report union move errors yet."),
86 MoveError::IllegalMove { cannot_move_out_of: o } => (o.span, o.kind),
88 let origin = Origin::Mir;
89 let mut err = match kind {
90 IllegalMoveOriginKind::Static =>
91 tcx.cannot_move_out_of(span, "static item", origin),
92 IllegalMoveOriginKind::BorrowedContent =>
93 tcx.cannot_move_out_of(span, "borrowed content", origin),
94 IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } =>
95 tcx.cannot_move_out_of_interior_of_drop(span, ty, origin),
96 IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } =>
97 tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin),
105 // Make our own copy of the MIR. This copy will be modified (in place) to
106 // contain non-lexical lifetimes. It will have a lifetime tied
107 // to the inference context.
108 let mut mir: Mir<'tcx> = input_mir.clone();
111 // If we are in non-lexical mode, compute the non-lexical lifetimes.
112 let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll {
115 Some(nll::compute_regions(infcx, def_id, param_env, mir))
118 let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
119 let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
120 let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
121 Borrows::new(tcx, mir, opt_regioncx.as_ref()),
122 |bd, i| bd.location(i));
123 let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
124 MaybeInitializedLvals::new(tcx, mir, &mdpe),
125 |bd, i| &bd.move_data().move_paths[i]);
126 let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
127 MaybeUninitializedLvals::new(tcx, mir, &mdpe),
128 |bd, i| &bd.move_data().move_paths[i]);
129 let flow_move_outs = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
130 MovingOutStatements::new(tcx, mir, &mdpe),
131 |bd, i| &bd.move_data().moves[i]);
132 let flow_ever_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
133 EverInitializedLvals::new(tcx, mir, &mdpe),
134 |bd, i| &bd.move_data().inits[i]);
136 let mut mbcx = MirBorrowckCtxt {
140 move_data: &mdpe.move_data,
141 param_env: param_env,
142 storage_dead_or_drop_error_reported: FxHashSet(),
145 let mut state = InProgress::new(flow_borrows,
151 mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
155 pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
156 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
158 node_id: ast::NodeId,
159 move_data: &'cx MoveData<'tcx>,
160 param_env: ParamEnv<'gcx>,
161 /// This field keeps track of when storage dead or drop errors are reported
162 /// in order to stop duplicate error reporting and identify the conditions required
163 /// for a "temporary value dropped here while still borrowed" error. See #45360.
164 storage_dead_or_drop_error_reported: FxHashSet<Local>,
167 // (forced to be `pub` due to its use as an associated type below.)
168 pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> {
169 borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
170 inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
171 uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
172 move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
173 ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
176 struct FlowInProgress<BD> where BD: BitDenotation {
177 base_results: DataflowResults<BD>,
178 curr_state: IdxSetBuf<BD::Idx>,
179 stmt_gen: IdxSetBuf<BD::Idx>,
180 stmt_kill: IdxSetBuf<BD::Idx>,
184 // 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
185 // 2. loans made in overlapping scopes do not conflict
186 // 3. assignments do not affect things loaned out as immutable
187 // 4. moves do not affect things loaned out in any way
188 impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
189 type FlowState = InProgress<'cx, 'gcx, 'tcx>;
191 fn mir(&self) -> &'cx Mir<'tcx> { self.mir }
193 fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
194 flow_state.each_flow(|b| b.reset_to_entry_of(bb),
195 |i| i.reset_to_entry_of(bb),
196 |u| u.reset_to_entry_of(bb),
197 |m| m.reset_to_entry_of(bb),
198 |e| e.reset_to_entry_of(bb));
201 fn reconstruct_statement_effect(&mut self,
203 flow_state: &mut Self::FlowState) {
204 flow_state.each_flow(|b| b.reconstruct_statement_effect(location),
205 |i| i.reconstruct_statement_effect(location),
206 |u| u.reconstruct_statement_effect(location),
207 |m| m.reconstruct_statement_effect(location),
208 |e| e.reconstruct_statement_effect(location));
211 fn apply_local_effect(&mut self,
213 flow_state: &mut Self::FlowState) {
214 flow_state.each_flow(|b| b.apply_local_effect(),
215 |i| i.apply_local_effect(),
216 |u| u.apply_local_effect(),
217 |m| m.apply_local_effect(),
218 |e| e.apply_local_effect());
221 fn reconstruct_terminator_effect(&mut self,
223 flow_state: &mut Self::FlowState) {
224 flow_state.each_flow(|b| b.reconstruct_terminator_effect(location),
225 |i| i.reconstruct_terminator_effect(location),
226 |u| u.reconstruct_terminator_effect(location),
227 |m| m.reconstruct_terminator_effect(location),
228 |e| e.reconstruct_terminator_effect(location));
231 fn visit_block_entry(&mut self,
233 flow_state: &Self::FlowState) {
234 let summary = flow_state.summary();
235 debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary);
238 fn visit_statement_entry(&mut self,
240 stmt: &Statement<'tcx>,
241 flow_state: &Self::FlowState) {
242 let summary = flow_state.summary();
243 debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary);
244 let span = stmt.source_info.span;
246 StatementKind::Assign(ref lhs, ref rhs) => {
247 // NOTE: NLL RFC calls for *shallow* write; using Deep
248 // for short-term compat w/ AST-borrowck. Also, switch
249 // to shallow requires to dataflow: "if this is an
250 // assignment `lv = <rvalue>`, then any loan for some
251 // path P of which `lv` is a prefix is killed."
252 self.mutate_lvalue(ContextKind::AssignLhs.new(location),
253 (lhs, span), Deep, JustWrite, flow_state);
255 self.consume_rvalue(ContextKind::AssignRhs.new(location),
256 (rhs, span), location, flow_state);
258 StatementKind::SetDiscriminant { ref lvalue, variant_index: _ } => {
259 self.mutate_lvalue(ContextKind::SetDiscrim.new(location),
261 Shallow(Some(ArtificialField::Discriminant)),
265 StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
266 let context = ContextKind::InlineAsm.new(location);
267 for (o, output) in asm.outputs.iter().zip(outputs) {
269 // FIXME(eddyb) indirect inline asm outputs should
270 // be encoeded through MIR lvalue derefs instead.
271 self.access_lvalue(context,
273 (Deep, Read(ReadKind::Copy)),
275 self.check_if_path_is_moved(context, InitializationRequiringAction::Use,
276 (output, span), flow_state);
278 self.mutate_lvalue(context,
281 if o.is_rw { WriteAndRead } else { JustWrite },
285 for input in inputs {
286 self.consume_operand(context, (input, span), flow_state);
289 StatementKind::EndRegion(ref _rgn) => {
290 // ignored when consuming results (update to
291 // flow_state already handled).
294 StatementKind::Validate(..) |
295 StatementKind::StorageLive(..) => {
296 // `Nop`, `Validate`, and `StorageLive` are irrelevant
300 StatementKind::StorageDead(local) => {
301 self.access_lvalue(ContextKind::StorageDead.new(location),
302 (&Lvalue::Local(local), span),
303 (Shallow(None), Write(WriteKind::StorageDeadOrDrop)), flow_state);
308 fn visit_terminator_entry(&mut self,
310 term: &Terminator<'tcx>,
311 flow_state: &Self::FlowState) {
313 let summary = flow_state.summary();
314 debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", location, term, summary);
315 let span = term.source_info.span;
317 TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => {
318 self.consume_operand(ContextKind::SwitchInt.new(loc),
319 (discr, span), flow_state);
321 TerminatorKind::Drop { location: ref drop_lvalue, target: _, unwind: _ } => {
322 self.access_lvalue(ContextKind::Drop.new(loc),
324 (Deep, Write(WriteKind::StorageDeadOrDrop)),
327 TerminatorKind::DropAndReplace { location: ref drop_lvalue,
328 value: ref new_value,
331 self.mutate_lvalue(ContextKind::DropAndReplace.new(loc),
336 self.consume_operand(ContextKind::DropAndReplace.new(loc),
337 (new_value, span), flow_state);
339 TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
340 self.consume_operand(ContextKind::CallOperator.new(loc),
341 (func, span), flow_state);
343 self.consume_operand(ContextKind::CallOperand.new(loc),
344 (arg, span), flow_state);
346 if let Some((ref dest, _/*bb*/)) = *destination {
347 self.mutate_lvalue(ContextKind::CallDest.new(loc),
354 TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
355 self.consume_operand(ContextKind::Assert.new(loc),
356 (cond, span), flow_state);
358 AssertMessage::BoundsCheck { ref len, ref index } => {
359 self.consume_operand(ContextKind::Assert.new(loc),
360 (len, span), flow_state);
361 self.consume_operand(ContextKind::Assert.new(loc),
362 (index, span), flow_state);
364 AssertMessage::Math(_/*const_math_err*/) => {}
365 AssertMessage::GeneratorResumedAfterReturn => {}
366 AssertMessage::GeneratorResumedAfterPanic => {}
370 TerminatorKind::Yield { ref value, resume: _, drop: _} => {
371 self.consume_operand(ContextKind::Yield.new(loc),
372 (value, span), flow_state);
375 TerminatorKind::Resume |
376 TerminatorKind::Return |
377 TerminatorKind::GeneratorDrop => {
378 // Returning from the function implicitly kills storage for all locals and statics.
379 // Often, the storage will already have been killed by an explicit
380 // StorageDead, but we don't always emit those (notably on unwind paths),
381 // so this "extra check" serves as a kind of backup.
382 let domain = flow_state.borrows.base_results.operator();
383 for borrow in domain.borrows() {
384 let root_lvalue = self.prefixes(
389 Lvalue::Static(_) => {
391 ContextKind::StorageDead.new(loc),
392 (&root_lvalue, self.mir.source_info(borrow.location).span),
393 (Deep, Write(WriteKind::StorageDeadOrDrop)),
397 Lvalue::Local(_) => {
399 ContextKind::StorageDead.new(loc),
400 (&root_lvalue, self.mir.source_info(borrow.location).span),
401 (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
405 Lvalue::Projection(_) => ()
409 TerminatorKind::Goto { target: _ } |
410 TerminatorKind::Unreachable |
411 TerminatorKind::FalseEdges { .. } => {
412 // no data used, thus irrelevant to borrowck
418 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
419 enum MutateMode { JustWrite, WriteAndRead }
421 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
422 enum Control { Continue, Break }
424 use self::ShallowOrDeep::{Shallow, Deep};
425 use self::ReadOrWrite::{Read, Write};
427 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
428 enum ArtificialField {
433 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
435 /// From the RFC: "A *shallow* access means that the immediate
436 /// fields reached at LV are accessed, but references or pointers
437 /// found within are not dereferenced. Right now, the only access
438 /// that is shallow is an assignment like `x = ...;`, which would
439 /// be a *shallow write* of `x`."
440 Shallow(Option<ArtificialField>),
442 /// From the RFC: "A *deep* access means that all data reachable
443 /// through the given lvalue may be invalidated or accesses by
448 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
450 /// From the RFC: "A *read* means that the existing data may be
451 /// read, but will not be changed."
454 /// From the RFC: "A *write* means that the data may be mutated to
455 /// new values or otherwise invalidated (for example, it could be
456 /// de-initialized, as in a move operation).
460 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
466 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
469 MutableBorrow(BorrowKind),
474 #[derive(Copy, Clone)]
475 enum InitializationRequiringAction {
482 impl InitializationRequiringAction {
483 fn as_noun(self) -> &'static str {
485 InitializationRequiringAction::Update => "update",
486 InitializationRequiringAction::Borrow => "borrow",
487 InitializationRequiringAction::Use => "use",
488 InitializationRequiringAction::Assignment => "assign"
492 fn as_verb_in_past_tense(self) -> &'static str {
494 InitializationRequiringAction::Update => "updated",
495 InitializationRequiringAction::Borrow => "borrowed",
496 InitializationRequiringAction::Use => "used",
497 InitializationRequiringAction::Assignment => "assigned"
502 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
503 /// Checks an access to the given lvalue to see if it is allowed. Examines the set of borrows
504 /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
505 /// lvalue is initialized and (b) it is not borrowed in some way that would prevent this
508 /// Returns true if an error is reported, false otherwise.
509 fn access_lvalue(&mut self,
511 lvalue_span: (&Lvalue<'tcx>, Span),
512 kind: (ShallowOrDeep, ReadOrWrite),
513 flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
516 let storage_dead_or_drop_local = match (lvalue_span.0, rw) {
517 (&Lvalue::Local(local), Write(WriteKind::StorageDeadOrDrop)) => Some(local),
521 // Check if error has already been reported to stop duplicate reporting.
522 if let Some(local) = storage_dead_or_drop_local {
523 if self.storage_dead_or_drop_error_reported.contains(&local) {
529 self.check_access_permissions(lvalue_span, rw);
531 let mut error_reported = false;
532 self.each_borrow_involving_path(
533 context, (sd, lvalue_span.0), flow_state, |this, _index, borrow, common_prefix| {
534 match (rw, borrow.kind) {
535 (Read(_), BorrowKind::Shared) => {
538 (Read(kind), BorrowKind::Unique) |
539 (Read(kind), BorrowKind::Mut) => {
542 error_reported = true;
543 this.report_use_while_mutably_borrowed(
544 context, lvalue_span, borrow)
546 ReadKind::Borrow(bk) => {
547 let end_issued_loan_span =
548 flow_state.borrows.base_results.operator().opt_region_end_span(
550 error_reported = true;
551 this.report_conflicting_borrow(
552 context, common_prefix, lvalue_span, bk,
553 &borrow, end_issued_loan_span)
558 (Write(kind), _) => {
560 WriteKind::MutableBorrow(bk) => {
561 let end_issued_loan_span =
562 flow_state.borrows.base_results.operator().opt_region_end_span(
564 error_reported = true;
565 this.report_conflicting_borrow(
566 context, common_prefix, lvalue_span, bk,
567 &borrow, end_issued_loan_span)
569 WriteKind::StorageDeadOrDrop => {
571 flow_state.borrows.base_results.operator().opt_region_end_span(
573 error_reported = true;
574 this.report_borrowed_value_does_not_live_long_enough(
575 context, lvalue_span, end_span)
577 WriteKind::Mutate => {
578 error_reported = true;
579 this.report_illegal_mutation_of_borrowed(
580 context, lvalue_span, borrow)
583 error_reported = true;
584 this.report_move_out_while_borrowed(
585 context, lvalue_span, &borrow)
594 if let Some(local) = storage_dead_or_drop_local {
595 self.storage_dead_or_drop_error_reported.insert(local);
600 fn mutate_lvalue(&mut self,
602 lvalue_span: (&Lvalue<'tcx>, Span),
605 flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
606 // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
608 MutateMode::WriteAndRead => {
609 self.check_if_path_is_moved(context, InitializationRequiringAction::Update,
610 lvalue_span, flow_state);
612 MutateMode::JustWrite => {
613 self.check_if_assigned_path_is_moved(context, lvalue_span, flow_state);
617 self.access_lvalue(context, lvalue_span, (kind, Write(WriteKind::Mutate)), flow_state);
619 // check for reassignments to immutable local variables
620 self.check_if_reassignment_to_immutable_state(context, lvalue_span, flow_state);
623 fn consume_rvalue(&mut self,
625 (rvalue, span): (&Rvalue<'tcx>, Span),
627 flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
629 Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => {
630 let access_kind = match bk {
631 BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
633 BorrowKind::Mut => (Deep, Write(WriteKind::MutableBorrow(bk))),
635 self.access_lvalue(context, (lvalue, span), access_kind, flow_state);
636 self.check_if_path_is_moved(context, InitializationRequiringAction::Borrow,
637 (lvalue, span), flow_state);
640 Rvalue::Use(ref operand) |
641 Rvalue::Repeat(ref operand, _) |
642 Rvalue::UnaryOp(_/*un_op*/, ref operand) |
643 Rvalue::Cast(_/*cast_kind*/, ref operand, _/*ty*/) => {
644 self.consume_operand(context, (operand, span), flow_state)
647 Rvalue::Len(ref lvalue) |
648 Rvalue::Discriminant(ref lvalue) => {
649 let af = match *rvalue {
650 Rvalue::Len(..) => ArtificialField::ArrayLength,
651 Rvalue::Discriminant(..) => ArtificialField::Discriminant,
655 context, (lvalue, span), (Shallow(Some(af)), Read(ReadKind::Copy)), flow_state);
656 self.check_if_path_is_moved(context, InitializationRequiringAction::Use,
657 (lvalue, span), flow_state);
660 Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) |
661 Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => {
662 self.consume_operand(context, (operand1, span), flow_state);
663 self.consume_operand(context, (operand2, span), flow_state);
666 Rvalue::NullaryOp(_op, _ty) => {
667 // nullary ops take no dynamic input; no borrowck effect.
669 // FIXME: is above actually true? Do we want to track
670 // the fact that uninitialized data can be created via
674 Rvalue::Aggregate(ref _aggregate_kind, ref operands) => {
675 for operand in operands {
676 self.consume_operand(context, (operand, span), flow_state);
682 fn consume_operand(&mut self,
684 (operand, span): (&Operand<'tcx>, Span),
685 flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
687 Operand::Copy(ref lvalue) => {
688 // copy of lvalue: check if this is "copy of frozen path"
689 // (FIXME: see check_loans.rs)
690 self.access_lvalue(context,
692 (Deep, Read(ReadKind::Copy)),
695 // Finally, check if path was already moved.
696 self.check_if_path_is_moved(context, InitializationRequiringAction::Use,
697 (lvalue, span), flow_state);
699 Operand::Move(ref lvalue) => {
700 // move of lvalue: check if this is move of already borrowed path
701 self.access_lvalue(context,
703 (Deep, Write(WriteKind::Move)),
706 // Finally, check if path was already moved.
707 self.check_if_path_is_moved(context, InitializationRequiringAction::Use,
708 (lvalue, span), flow_state);
710 Operand::Constant(_) => {}
715 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
716 fn check_if_reassignment_to_immutable_state(&mut self,
718 (lvalue, span): (&Lvalue<'tcx>, Span),
719 flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
720 let move_data = self.move_data;
722 // determine if this path has a non-mut owner (and thus needs checking).
726 Lvalue::Projection(ref proj) => {
730 Lvalue::Local(local) => {
731 match self.mir.local_decls[local].mutability {
732 Mutability::Not => break, // needs check
733 Mutability::Mut => return,
736 Lvalue::Static(ref static_) => {
737 // mutation of non-mut static is always illegal,
738 // independent of dataflow.
739 if !self.tcx.is_static_mut(static_.def_id) {
740 self.report_assignment_to_static(context, (lvalue, span));
747 if let Some(mpi) = self.move_path_for_lvalue(lvalue) {
748 for ii in &move_data.init_path_map[mpi] {
749 if flow_state.ever_inits.curr_state.contains(ii) {
750 let first_assign_span = self.move_data.inits[*ii].span;
751 self.report_illegal_reassignment(
752 context, (lvalue, span), first_assign_span);
759 fn check_if_path_is_moved(&mut self,
761 desired_action: InitializationRequiringAction,
762 lvalue_span: (&Lvalue<'tcx>, Span),
763 flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
764 // FIXME: analogous code in check_loans first maps `lvalue` to
765 // its base_path ... but is that what we want here?
766 let lvalue = self.base_path(lvalue_span.0);
768 let maybe_uninits = &flow_state.uninits;
769 let curr_move_outs = &flow_state.move_outs.curr_state;
773 // 1. Move of `a.b.c`, use of `a.b.c`
774 // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
775 // 3. Move of `a.b.c`, use of `a` or `a.b`
776 // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
777 // partial initialization support, one might have `a.x`
778 // initialized but not `a.b`.
782 // 5. Move of `a.b.c`, use of `a.b.d`
783 // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
784 // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
785 // must have been initialized for the use to be sound.
786 // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
788 // The dataflow tracks shallow prefixes distinctly (that is,
789 // field-accesses on P distinctly from P itself), in order to
790 // track substructure initialization separately from the whole
793 // E.g., when looking at (*a.b.c).d, if the closest prefix for
794 // which we have a MovePath is `a.b`, then that means that the
795 // initialization state of `a.b` is all we need to inspect to
796 // know if `a.b.c` is valid (and from that we infer that the
797 // dereference and `.d` access is also valid, since we assume
798 // `a.b.c` is assigned a reference to a initialized and
799 // well-formed record structure.)
801 // Therefore, if we seek out the *closest* prefix for which we
802 // have a MovePath, that should capture the initialization
803 // state for the lvalue scenario.
805 // This code covers scenarios 1, 2, and 4.
807 debug!("check_if_path_is_moved part1 lvalue: {:?}", lvalue);
808 match self.move_path_closest_to(lvalue) {
810 if maybe_uninits.curr_state.contains(&mpi) {
811 self.report_use_of_moved_or_uninitialized(context, desired_action,
814 return; // don't bother finding other problems.
817 Err(NoMovePathFound::ReachedStatic) => {
818 // Okay: we do not build MoveData for static variables
821 // Only query longest prefix with a MovePath, not further
822 // ancestors; dataflow recurs on children when parents
823 // move (to support partial (re)inits).
825 // (I.e. querying parents breaks scenario 8; but may want
826 // to do such a query based on partial-init feature-gate.)
829 // A move of any shallow suffix of `lvalue` also interferes
830 // with an attempt to use `lvalue`. This is scenario 3 above.
832 // (Distinct from handling of scenarios 1+2+4 above because
833 // `lvalue` does not interfere with suffixes of its prefixes,
834 // e.g. `a.b.c` does not interfere with `a.b.d`)
836 debug!("check_if_path_is_moved part2 lvalue: {:?}", lvalue);
837 if let Some(mpi) = self.move_path_for_lvalue(lvalue) {
838 if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) {
839 self.report_use_of_moved_or_uninitialized(context, desired_action,
840 lvalue_span, child_mpi,
842 return; // don't bother finding other problems.
847 /// Currently MoveData does not store entries for all lvalues in
848 /// the input MIR. For example it will currently filter out
849 /// lvalues that are Copy; thus we do not track lvalues of shared
850 /// reference type. This routine will walk up an lvalue along its
851 /// prefixes, searching for a foundational lvalue that *is*
852 /// tracked in the MoveData.
854 /// An Err result includes a tag indicated why the search failed.
855 /// Currenly this can only occur if the lvalue is built off of a
856 /// static variable, as we do not track those in the MoveData.
857 fn move_path_closest_to(&mut self, lvalue: &Lvalue<'tcx>)
858 -> Result<MovePathIndex, NoMovePathFound>
860 let mut last_prefix = lvalue;
861 for prefix in self.prefixes(lvalue, PrefixSet::All) {
862 if let Some(mpi) = self.move_path_for_lvalue(prefix) {
865 last_prefix = prefix;
868 Lvalue::Local(_) => panic!("should have move path for every Local"),
869 Lvalue::Projection(_) => panic!("PrefixSet::All meant dont stop for Projection"),
870 Lvalue::Static(_) => return Err(NoMovePathFound::ReachedStatic),
874 fn move_path_for_lvalue(&mut self,
875 lvalue: &Lvalue<'tcx>)
876 -> Option<MovePathIndex>
878 // If returns None, then there is no move path corresponding
879 // to a direct owner of `lvalue` (which means there is nothing
880 // that borrowck tracks for its analysis).
882 match self.move_data.rev_lookup.find(lvalue) {
883 LookupResult::Parent(_) => None,
884 LookupResult::Exact(mpi) => Some(mpi),
888 fn check_if_assigned_path_is_moved(&mut self,
890 (lvalue, span): (&Lvalue<'tcx>, Span),
891 flow_state: &InProgress<'cx, 'gcx, 'tcx>) {
892 // recur down lvalue; dispatch to check_if_path_is_moved when necessary
893 let mut lvalue = lvalue;
896 Lvalue::Local(_) | Lvalue::Static(_) => {
897 // assigning to `x` does not require `x` be initialized.
900 Lvalue::Projection(ref proj) => {
901 let Projection { ref base, ref elem } = **proj;
903 ProjectionElem::Deref |
904 // assigning to *P requires `P` initialized.
905 ProjectionElem::Index(_/*operand*/) |
906 ProjectionElem::ConstantIndex { .. } |
907 // assigning to P[i] requires `P` initialized.
908 ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
909 // assigning to (P->variant) is okay if assigning to `P` is okay
911 // FIXME: is this true even if P is a adt with a dtor?
914 ProjectionElem::Subslice { .. } => {
915 panic!("we dont allow assignments to subslices, context: {:?}",
919 ProjectionElem::Field(..) => {
920 // if type of `P` has a dtor, then
921 // assigning to `P.f` requires `P` itself
922 // be already initialized
924 match base.ty(self.mir, tcx).to_ty(tcx).sty {
925 ty::TyAdt(def, _) if def.has_dtor(tcx) => {
927 // FIXME: analogous code in
928 // check_loans.rs first maps
929 // `base` to its base_path.
931 self.check_if_path_is_moved(
932 context, InitializationRequiringAction::Assignment,
933 (base, span), flow_state);
935 // (base initialized; no need to
951 /// Check the permissions for the given lvalue and read or write kind
952 fn check_access_permissions(&self, (lvalue, span): (&Lvalue<'tcx>, Span), kind: ReadOrWrite) {
954 Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => {
955 if let Err(_lvalue_err) = self.is_unique(lvalue) {
956 span_bug!(span, "&unique borrow for `{}` should not fail",
957 self.describe_lvalue(lvalue));
960 Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => {
961 if let Err(lvalue_err) = self.is_mutable(lvalue) {
962 let mut err = self.tcx.cannot_borrow_path_as_mutable(span,
963 &format!("immutable item `{}`",
964 self.describe_lvalue(lvalue)),
966 err.span_label(span, "cannot borrow as mutable");
968 if lvalue != lvalue_err {
969 err.note(&format!("Value not mutable causing this error: `{}`",
970 self.describe_lvalue(lvalue_err)));
976 _ => {}// Access authorized
980 /// Can this value be written or borrowed mutably
981 fn is_mutable<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> Result<(), &'d Lvalue<'tcx>> {
983 Lvalue::Local(local) => {
984 let local = &self.mir.local_decls[local];
985 match local.mutability {
986 Mutability::Not => Err(lvalue),
987 Mutability::Mut => Ok(())
990 Lvalue::Static(ref static_) => {
991 if !self.tcx.is_static_mut(static_.def_id) {
997 Lvalue::Projection(ref proj) => {
999 ProjectionElem::Deref => {
1000 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
1002 // `Box<T>` owns its content, so mutable if its location is mutable
1003 if base_ty.is_box() {
1004 return self.is_mutable(&proj.base);
1007 // Otherwise we check the kind of deref to decide
1009 ty::TyRef(_, tnm) => {
1011 // Shared borrowed data is never mutable
1012 hir::MutImmutable => Err(lvalue),
1013 // Mutably borrowed data is mutable, but only if we have a
1014 // unique path to the `&mut`
1015 hir::MutMutable => self.is_unique(&proj.base),
1018 ty::TyRawPtr(tnm) => {
1020 // `*const` raw pointers are not mutable
1021 hir::MutImmutable => Err(lvalue),
1022 // `*mut` raw pointers are always mutable, regardless of context
1023 // The users have to check by themselve.
1024 hir::MutMutable => Ok(()),
1027 // Deref should only be for reference, pointers or boxes
1028 _ => bug!("Deref of unexpected type: {:?}", base_ty)
1031 // All other projections are owned by their base path, so mutable if
1032 // base path is mutable
1033 ProjectionElem::Field(..) |
1034 ProjectionElem::Index(..) |
1035 ProjectionElem::ConstantIndex{..} |
1036 ProjectionElem::Subslice{..} |
1037 ProjectionElem::Downcast(..) =>
1038 self.is_mutable(&proj.base)
1044 /// Does this lvalue have a unique path
1045 fn is_unique<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> Result<(), &'d Lvalue<'tcx>> {
1047 Lvalue::Local(..) => {
1048 // Local variables are unique
1051 Lvalue::Static(..) => {
1052 // Static variables are not
1055 Lvalue::Projection(ref proj) => {
1057 ProjectionElem::Deref => {
1058 let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
1060 // `Box<T>` referent is unique if box is a unique spot
1061 if base_ty.is_box() {
1062 return self.is_unique(&proj.base);
1065 // Otherwise we check the kind of deref to decide
1067 ty::TyRef(_, tnm) => {
1069 // lvalue represent an aliased location
1070 hir::MutImmutable => Err(lvalue),
1071 // `&mut T` is as unique as the context in which it is found
1072 hir::MutMutable => self.is_unique(&proj.base),
1075 ty::TyRawPtr(tnm) => {
1077 // `*mut` can be aliased, but we leave it to user
1078 hir::MutMutable => Ok(()),
1079 // `*const` is treated the same as `*mut`
1080 hir::MutImmutable => Ok(()),
1083 // Deref should only be for reference, pointers or boxes
1084 _ => bug!("Deref of unexpected type: {:?}", base_ty)
1087 // Other projections are unique if the base is unique
1088 ProjectionElem::Field(..) |
1089 ProjectionElem::Index(..) |
1090 ProjectionElem::ConstantIndex{..} |
1091 ProjectionElem::Subslice{..} |
1092 ProjectionElem::Downcast(..) =>
1093 self.is_unique(&proj.base)
1100 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1101 enum NoMovePathFound {
1105 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1106 fn each_borrow_involving_path<F>(&mut self,
1108 access_lvalue: (ShallowOrDeep, &Lvalue<'tcx>),
1109 flow_state: &InProgress<'cx, 'gcx, 'tcx>,
1111 where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Lvalue<'tcx>) -> Control
1113 let (access, lvalue) = access_lvalue;
1115 // FIXME: analogous code in check_loans first maps `lvalue` to
1118 let domain = flow_state.borrows.base_results.operator();
1119 let data = domain.borrows();
1121 // check for loan restricting path P being used. Accounts for
1122 // borrows of P, P.a.b, etc.
1123 'next_borrow: for i in flow_state.borrows.elems_incoming() {
1124 let borrowed = &data[i];
1126 // Is `lvalue` (or a prefix of it) already borrowed? If
1127 // so, that's relevant.
1129 // FIXME: Differs from AST-borrowck; includes drive-by fix
1130 // to #38899. Will probably need back-compat mode flag.
1131 for accessed_prefix in self.prefixes(lvalue, PrefixSet::All) {
1132 if *accessed_prefix == borrowed.lvalue {
1133 // FIXME: pass in enum describing case we are in?
1134 let ctrl = op(self, i, borrowed, accessed_prefix);
1135 if ctrl == Control::Break { return; }
1139 // Is `lvalue` a prefix (modulo access type) of the
1140 // `borrowed.lvalue`? If so, that's relevant.
1142 let prefix_kind = match access {
1143 Shallow(Some(ArtificialField::Discriminant)) |
1144 Shallow(Some(ArtificialField::ArrayLength)) => {
1145 // The discriminant and array length are like
1146 // additional fields on the type; they do not
1147 // overlap any existing data there. Furthermore,
1148 // they cannot actually be a prefix of any
1149 // borrowed lvalue (at least in MIR as it is
1151 continue 'next_borrow;
1153 Shallow(None) => PrefixSet::Shallow,
1154 Deep => PrefixSet::Supporting,
1157 for borrowed_prefix in self.prefixes(&borrowed.lvalue, prefix_kind) {
1158 if borrowed_prefix == lvalue {
1159 // FIXME: pass in enum describing case we are in?
1160 let ctrl = op(self, i, borrowed, borrowed_prefix);
1161 if ctrl == Control::Break { return; }
1168 use self::prefixes::PrefixSet;
1170 /// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an
1171 /// lvalue are formed by stripping away fields and derefs, except that
1172 /// we stop when we reach the deref of a shared reference. [...] "
1174 /// "Shallow prefixes are found by stripping away fields, but stop at
1175 /// any dereference. So: writing a path like `a` is illegal if `a.b`
1176 /// is borrowed. But: writing `a` is legal if `*a` is borrowed,
1177 /// whether or not `a` is a shared or mutable reference. [...] "
1179 use super::{MirBorrowckCtxt};
1182 use rustc::ty::{self, TyCtxt};
1183 use rustc::mir::{Lvalue, Mir, ProjectionElem};
1185 pub trait IsPrefixOf<'tcx> {
1186 fn is_prefix_of(&self, other: &Lvalue<'tcx>) -> bool;
1189 impl<'tcx> IsPrefixOf<'tcx> for Lvalue<'tcx> {
1190 fn is_prefix_of(&self, other: &Lvalue<'tcx>) -> bool {
1191 let mut cursor = other;
1199 Lvalue::Static(_) => return false,
1200 Lvalue::Projection(ref proj) => {
1201 cursor = &proj.base;
1209 pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
1210 mir: &'cx Mir<'tcx>,
1211 tcx: TyCtxt<'cx, 'gcx, 'tcx>,
1213 next: Option<&'cx Lvalue<'tcx>>,
1216 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1217 pub(super) enum PrefixSet {
1218 /// Doesn't stop until it returns the base case (a Local or
1221 /// Stops at any dereference.
1223 /// Stops at the deref of a shared reference.
1227 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1228 /// Returns an iterator over the prefixes of `lvalue`
1229 /// (inclusive) from longest to smallest, potentially
1230 /// terminating the iteration early based on `kind`.
1231 pub(super) fn prefixes(&self,
1232 lvalue: &'cx Lvalue<'tcx>,
1234 -> Prefixes<'cx, 'gcx, 'tcx>
1236 Prefixes { next: Some(lvalue), kind, mir: self.mir, tcx: self.tcx }
1240 impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> {
1241 type Item = &'cx Lvalue<'tcx>;
1242 fn next(&mut self) -> Option<Self::Item> {
1243 let mut cursor = match self.next {
1244 None => return None,
1245 Some(lvalue) => lvalue,
1248 // Post-processing `lvalue`: Enqueue any remaining
1249 // work. Also, `lvalue` may not be a prefix itself, but
1250 // may hold one further down (e.g. we never return
1251 // downcasts here, but may return a base of a downcast).
1254 let proj = match *cursor {
1255 Lvalue::Local(_) | // search yielded this leaf
1256 Lvalue::Static(_) => {
1258 return Some(cursor);
1261 Lvalue::Projection(ref proj) => proj,
1265 ProjectionElem::Field(_/*field*/, _/*ty*/) => {
1266 // FIXME: add union handling
1267 self.next = Some(&proj.base);
1268 return Some(cursor);
1270 ProjectionElem::Downcast(..) |
1271 ProjectionElem::Subslice { .. } |
1272 ProjectionElem::ConstantIndex { .. } |
1273 ProjectionElem::Index(_) => {
1274 cursor = &proj.base;
1277 ProjectionElem::Deref => {
1282 assert_eq!(proj.elem, ProjectionElem::Deref);
1285 PrefixSet::Shallow => {
1286 // shallow prefixes are found by stripping away
1287 // fields, but stop at *any* dereference.
1288 // So we can just stop the traversal now.
1290 return Some(cursor);
1293 // all prefixes: just blindly enqueue the base
1294 // of the projection
1295 self.next = Some(&proj.base);
1296 return Some(cursor);
1298 PrefixSet::Supporting => {
1303 assert_eq!(self.kind, PrefixSet::Supporting);
1304 // supporting prefixes: strip away fields and
1305 // derefs, except we stop at the deref of a shared
1308 let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
1311 ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
1312 // don't continue traversing over derefs of raw pointers or shared borrows.
1314 return Some(cursor);
1317 ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
1318 self.next = Some(&proj.base);
1319 return Some(cursor);
1322 ty::TyAdt(..) if ty.is_box() => {
1323 self.next = Some(&proj.base);
1324 return Some(cursor);
1327 _ => panic!("unknown type fed to Projection Deref."),
1334 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1335 fn report_use_of_moved_or_uninitialized(&mut self,
1337 desired_action: InitializationRequiringAction,
1338 (lvalue, span): (&Lvalue<'tcx>, Span),
1340 curr_move_out: &IdxSetBuf<MoveOutIndex>) {
1342 let mois = self.move_data.path_map[mpi].iter().filter(
1343 |moi| curr_move_out.contains(moi)).collect::<Vec<_>>();
1345 if mois.is_empty() {
1346 self.tcx.cannot_act_on_uninitialized_variable(span,
1347 desired_action.as_noun(),
1348 &self.describe_lvalue(lvalue),
1350 .span_label(span, format!("use of possibly uninitialized `{}`",
1351 self.describe_lvalue(lvalue)))
1354 let msg = ""; //FIXME: add "partially " or "collaterally "
1356 let mut err = self.tcx.cannot_act_on_moved_value(span,
1357 desired_action.as_noun(),
1359 &self.describe_lvalue(lvalue),
1362 err.span_label(span, format!("value {} here after move",
1363 desired_action.as_verb_in_past_tense()));
1365 let move_msg = ""; //FIXME: add " (into closure)"
1366 let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
1367 if span == move_span {
1368 err.span_label(span,
1369 format!("value moved{} here in previous iteration of loop",
1372 err.span_label(move_span, format!("value moved{} here", move_msg));
1375 //FIXME: add note for closure
1380 fn report_move_out_while_borrowed(&mut self,
1382 (lvalue, span): (&Lvalue<'tcx>, Span),
1383 borrow: &BorrowData<'tcx>) {
1384 self.tcx.cannot_move_when_borrowed(span,
1385 &self.describe_lvalue(lvalue),
1387 .span_label(self.retrieve_borrow_span(borrow),
1388 format!("borrow of `{}` occurs here",
1389 self.describe_lvalue(&borrow.lvalue)))
1390 .span_label(span, format!("move out of `{}` occurs here",
1391 self.describe_lvalue(lvalue)))
1395 fn report_use_while_mutably_borrowed(&mut self,
1397 (lvalue, span): (&Lvalue<'tcx>, Span),
1398 borrow : &BorrowData<'tcx>) {
1400 let mut err = self.tcx.cannot_use_when_mutably_borrowed(
1401 span, &self.describe_lvalue(lvalue),
1402 self.retrieve_borrow_span(borrow), &self.describe_lvalue(&borrow.lvalue),
1408 /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
1409 /// the local assigned at `location`.
1410 /// This is done by searching in statements succeeding `location`
1411 /// and originating from `maybe_closure_span`.
1412 fn find_closure_span(
1414 maybe_closure_span: Span,
1416 ) -> Option<(Span, Span)> {
1417 use rustc::hir::ExprClosure;
1418 use rustc::mir::AggregateKind;
1420 let local = if let StatementKind::Assign(Lvalue::Local(local), _) =
1421 self.mir[location.block].statements[location.statement_index].kind
1428 for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
1429 if maybe_closure_span != stmt.source_info.span {
1433 if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref lvs)) = stmt.kind {
1434 if let AggregateKind::Closure(def_id, _) = **kind {
1435 debug!("find_closure_span: found closure {:?}", lvs);
1437 return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
1438 let args_span = if let ExprClosure(_, _, _, span, _) =
1439 self.tcx.hir.expect_expr(node_id).node
1446 self.tcx.with_freevars(node_id, |freevars| {
1447 for (v, lv) in freevars.iter().zip(lvs) {
1449 Operand::Copy(Lvalue::Local(l)) |
1450 Operand::Move(Lvalue::Local(l)) if local == l => {
1452 "find_closure_span: found captured local {:?}",
1455 return Some(v.span);
1461 }).map(|var_span| (args_span, var_span))
1472 fn report_conflicting_borrow(&mut self,
1474 common_prefix: &Lvalue<'tcx>,
1475 (lvalue, span): (&Lvalue<'tcx>, Span),
1476 gen_borrow_kind: BorrowKind,
1477 issued_borrow: &BorrowData,
1478 end_issued_loan_span: Option<Span>) {
1479 use self::prefixes::IsPrefixOf;
1481 assert!(common_prefix.is_prefix_of(lvalue));
1482 assert!(common_prefix.is_prefix_of(&issued_borrow.lvalue));
1484 let issued_span = self.retrieve_borrow_span(issued_borrow);
1486 let new_closure_span = self.find_closure_span(span, context.loc);
1487 let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
1488 let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location);
1489 let issued_span = old_closure_span.map(|(args, _)| args).unwrap_or(issued_span);
1491 let desc_lvalue = self.describe_lvalue(lvalue);
1493 // FIXME: supply non-"" `opt_via` when appropriate
1494 let mut err = match (gen_borrow_kind, "immutable", "mutable",
1495 issued_borrow.kind, "immutable", "mutable") {
1496 (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
1497 (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) =>
1498 self.tcx.cannot_reborrow_already_borrowed(
1499 span, &desc_lvalue, "", lft, issued_span,
1500 "it", rgt, "", end_issued_loan_span, Origin::Mir),
1502 (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) =>
1503 self.tcx.cannot_mutably_borrow_multiply(
1504 span, &desc_lvalue, "", issued_span,
1505 "", end_issued_loan_span, Origin::Mir),
1507 (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) =>
1508 self.tcx.cannot_uniquely_borrow_by_two_closures(
1509 span, &desc_lvalue, issued_span,
1510 end_issued_loan_span, Origin::Mir),
1512 (BorrowKind::Unique, _, _, _, _, _) =>
1513 self.tcx.cannot_uniquely_borrow_by_one_closure(
1514 span, &desc_lvalue, "",
1515 issued_span, "it", "", end_issued_loan_span, Origin::Mir),
1517 (_, _, _, BorrowKind::Unique, _, _) =>
1518 self.tcx.cannot_reborrow_already_uniquely_borrowed(
1519 span, &desc_lvalue, "it", "",
1520 issued_span, "", end_issued_loan_span, Origin::Mir),
1522 (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) =>
1526 if let Some((_, var_span)) = old_closure_span {
1529 format!("previous borrow occurs due to use of `{}` in closure", desc_lvalue),
1533 if let Some((_, var_span)) = new_closure_span {
1536 format!("borrow occurs due to use of `{}` in closure", desc_lvalue),
1543 fn report_borrowed_value_does_not_live_long_enough(&mut self,
1545 (lvalue, span): (&Lvalue, Span),
1546 end_span: Option<Span>) {
1547 let proper_span = match *lvalue {
1548 Lvalue::Local(local) => self.mir.local_decls[local].source_info.span,
1552 let mut err = self.tcx.path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
1553 err.span_label(proper_span, "temporary value created here");
1554 err.span_label(span, "temporary value dropped here while still borrowed");
1555 err.note("consider using a `let` binding to increase its lifetime");
1557 if let Some(end) = end_span {
1558 err.span_label(end, "temporary value needs to live until here");
1564 fn report_illegal_mutation_of_borrowed(&mut self,
1566 (lvalue, span): (&Lvalue<'tcx>, Span),
1567 loan: &BorrowData) {
1568 let mut err = self.tcx.cannot_assign_to_borrowed(
1569 span, self.retrieve_borrow_span(loan), &self.describe_lvalue(lvalue), Origin::Mir);
1574 fn report_illegal_reassignment(&mut self,
1576 (lvalue, span): (&Lvalue<'tcx>, Span),
1577 assigned_span: Span) {
1578 let mut err = self.tcx.cannot_reassign_immutable(span,
1579 &self.describe_lvalue(lvalue),
1581 err.span_label(span, "cannot assign twice to immutable variable");
1582 if span != assigned_span {
1583 err.span_label(assigned_span, format!("first assignment to `{}`",
1584 self.describe_lvalue(lvalue)));
1589 fn report_assignment_to_static(&mut self,
1591 (lvalue, span): (&Lvalue<'tcx>, Span)) {
1592 let mut err = self.tcx.cannot_assign_static(
1593 span, &self.describe_lvalue(lvalue), Origin::Mir);
1598 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1599 // End-user visible description of `lvalue`
1600 fn describe_lvalue(&self, lvalue: &Lvalue<'tcx>) -> String {
1601 let mut buf = String::new();
1602 self.append_lvalue_to_string(lvalue, &mut buf, false);
1606 /// If this is a field projection, and the field is being projected from a closure type,
1607 /// then returns the index of the field being projected. Note that this closure will always
1608 /// be `self` in the current MIR, because that is the only time we directly access the fields
1609 /// of a closure type.
1610 fn is_upvar_field_projection(&self, lvalue: &Lvalue<'tcx>) -> Option<Field> {
1612 Lvalue::Projection(ref proj) => {
1614 ProjectionElem::Field(field, _ty) => {
1615 let is_projection_from_ty_closure = proj.base.ty(self.mir, self.tcx)
1616 .to_ty(self.tcx).is_closure();
1618 if is_projection_from_ty_closure {
1631 // Appends end-user visible description of `lvalue` to `buf`.
1632 fn append_lvalue_to_string(&self,
1633 lvalue: &Lvalue<'tcx>,
1635 mut autoderef: bool) {
1637 Lvalue::Local(local) => {
1638 self.append_local_to_string(local, buf, "_");
1640 Lvalue::Static(ref static_) => {
1641 buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
1643 Lvalue::Projection(ref proj) => {
1645 ProjectionElem::Deref => {
1646 if let Some(field) = self.is_upvar_field_projection(&proj.base) {
1647 let var_index = field.index();
1648 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
1649 if self.mir.upvar_decls[var_index].by_ref {
1650 buf.push_str(&name);
1652 buf.push_str(&format!("*{}", &name));
1656 self.append_lvalue_to_string(&proj.base, buf, autoderef);
1659 self.append_lvalue_to_string(&proj.base, buf, autoderef);
1663 ProjectionElem::Downcast(..) => {
1664 self.append_lvalue_to_string(&proj.base, buf, autoderef);
1666 ProjectionElem::Field(field, _ty) => {
1669 if let Some(field) = self.is_upvar_field_projection(lvalue) {
1670 let var_index = field.index();
1671 let name = self.mir.upvar_decls[var_index].debug_name.to_string();
1672 buf.push_str(&name);
1674 let field_name = self.describe_field(&proj.base, field);
1675 self.append_lvalue_to_string(&proj.base, buf, autoderef);
1676 buf.push_str(&format!(".{}", field_name));
1679 ProjectionElem::Index(index) => {
1682 self.append_lvalue_to_string(&proj.base, buf, autoderef);
1684 self.append_local_to_string(index, buf, "..");
1687 ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
1689 // Since it isn't possible to borrow an element on a particular index and
1690 // then use another while the borrow is held, don't output indices details
1691 // to avoid confusing the end-user
1692 self.append_lvalue_to_string(&proj.base, buf, autoderef);
1693 buf.push_str(&"[..]");
1700 // Appends end-user visible description of the `local` lvalue to `buf`. If `local` doesn't have
1701 // a name, then `none_string` is appended instead
1702 fn append_local_to_string(&self, local_index: Local, buf: &mut String, none_string: &str) {
1703 let local = &self.mir.local_decls[local_index];
1705 Some(name) => buf.push_str(&format!("{}", name)),
1706 None => buf.push_str(none_string)
1710 // End-user visible description of the `field`nth field of `base`
1711 fn describe_field(&self, base: &Lvalue, field: Field) -> String {
1713 Lvalue::Local(local) => {
1714 let local = &self.mir.local_decls[local];
1715 self.describe_field_from_ty(&local.ty, field)
1717 Lvalue::Static(ref static_) => {
1718 self.describe_field_from_ty(&static_.ty, field)
1720 Lvalue::Projection(ref proj) => {
1722 ProjectionElem::Deref =>
1723 self.describe_field(&proj.base, field),
1724 ProjectionElem::Downcast(def, variant_index) =>
1725 format!("{}", def.variants[variant_index].fields[field.index()].name),
1726 ProjectionElem::Field(_, field_type) =>
1727 self.describe_field_from_ty(&field_type, field),
1728 ProjectionElem::Index(..)
1729 | ProjectionElem::ConstantIndex { .. }
1730 | ProjectionElem::Subslice { .. } =>
1731 format!("{}", self.describe_field(&proj.base, field)),
1737 // End-user visible description of the `field_index`nth field of `ty`
1738 fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
1740 // If the type is a box, the field is described from the boxed type
1741 self.describe_field_from_ty(&ty.boxed_ty(), field)
1745 ty::TyAdt(def, _) => {
1747 format!("{}", field.index())
1750 format!("{}", def.struct_variant().fields[field.index()].name)
1753 ty::TyTuple(_, _) => {
1754 format!("{}", field.index())
1756 ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => {
1757 self.describe_field_from_ty(&tnm.ty, field)
1759 ty::TyArray(ty, _) | ty::TySlice(ty) => {
1760 self.describe_field_from_ty(&ty, field)
1762 ty::TyClosure(closure_def_id, _) => {
1763 // Convert the def-id into a node-id. node-ids are only valid for
1764 // the local code in the current crate, so this returns an `Option` in case
1765 // the closure comes from another crate. But in that case we wouldn't
1766 // be borrowck'ing it, so we can just unwrap:
1767 let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
1768 let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
1770 self.tcx.hir.name(freevar.var_id()).to_string()
1773 // Might need a revision when the fields in trait RFC is implemented
1774 // (https://github.com/rust-lang/rfcs/pull/1546)
1775 bug!("End-user description not implemented for field access on `{:?}`", ty.sty);
1781 // Retrieve span of given borrow from the current MIR representation
1782 fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
1783 self.mir.source_info(borrow.location).span
1787 impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
1788 // FIXME (#16118): function intended to allow the borrow checker
1789 // to be less precise in its handling of Box while still allowing
1790 // moves out of a Box. They should be removed when/if we stop
1791 // treating Box specially (e.g. when/if DerefMove is added...)
1793 fn base_path<'d>(&self, lvalue: &'d Lvalue<'tcx>) -> &'d Lvalue<'tcx> {
1794 //! Returns the base of the leftmost (deepest) dereference of an
1795 //! Box in `lvalue`. If there is no dereference of an Box
1796 //! in `lvalue`, then it just returns `lvalue` itself.
1798 let mut cursor = lvalue;
1799 let mut deepest = lvalue;
1801 let proj = match *cursor {
1802 Lvalue::Local(..) | Lvalue::Static(..) => return deepest,
1803 Lvalue::Projection(ref proj) => proj,
1805 if proj.elem == ProjectionElem::Deref &&
1806 lvalue.ty(self.mir, self.tcx).to_ty(self.tcx).is_box()
1808 deepest = &proj.base;
1810 cursor = &proj.base;
1815 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1821 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1839 fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } }
1842 impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
1843 pub(super) fn new(borrows: DataflowResults<Borrows<'b, 'gcx, 'tcx>>,
1844 inits: DataflowResults<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
1845 uninits: DataflowResults<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
1846 move_out: DataflowResults<MovingOutStatements<'b, 'gcx, 'tcx>>,
1847 ever_inits: DataflowResults<EverInitializedLvals<'b, 'gcx, 'tcx>>)
1850 borrows: FlowInProgress::new(borrows),
1851 inits: FlowInProgress::new(inits),
1852 uninits: FlowInProgress::new(uninits),
1853 move_outs: FlowInProgress::new(move_out),
1854 ever_inits: FlowInProgress::new(ever_inits)
1858 fn each_flow<XB, XI, XU, XM, XE>(&mut self,
1859 mut xform_borrows: XB,
1860 mut xform_inits: XI,
1861 mut xform_uninits: XU,
1862 mut xform_move_outs: XM,
1863 mut xform_ever_inits: XE) where
1864 XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
1865 XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
1866 XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
1867 XM: FnMut(&mut FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>),
1868 XE: FnMut(&mut FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>),
1870 xform_borrows(&mut self.borrows);
1871 xform_inits(&mut self.inits);
1872 xform_uninits(&mut self.uninits);
1873 xform_move_outs(&mut self.move_outs);
1874 xform_ever_inits(&mut self.ever_inits);
1877 fn summary(&self) -> String {
1878 let mut s = String::new();
1880 s.push_str("borrows in effect: [");
1881 let mut saw_one = false;
1882 self.borrows.each_state_bit(|borrow| {
1883 if saw_one { s.push_str(", "); };
1885 let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
1886 s.push_str(&format!("{}", borrow_data));
1890 s.push_str("borrows generated: [");
1891 let mut saw_one = false;
1892 self.borrows.each_gen_bit(|borrow| {
1893 if saw_one { s.push_str(", "); };
1895 let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
1896 s.push_str(&format!("{}", borrow_data));
1900 s.push_str("inits: [");
1901 let mut saw_one = false;
1902 self.inits.each_state_bit(|mpi_init| {
1903 if saw_one { s.push_str(", "); };
1906 &self.inits.base_results.operator().move_data().move_paths[mpi_init];
1907 s.push_str(&format!("{}", move_path));
1911 s.push_str("uninits: [");
1912 let mut saw_one = false;
1913 self.uninits.each_state_bit(|mpi_uninit| {
1914 if saw_one { s.push_str(", "); };
1917 &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit];
1918 s.push_str(&format!("{}", move_path));
1922 s.push_str("move_out: [");
1923 let mut saw_one = false;
1924 self.move_outs.each_state_bit(|mpi_move_out| {
1925 if saw_one { s.push_str(", "); };
1928 &self.move_outs.base_results.operator().move_data().moves[mpi_move_out];
1929 s.push_str(&format!("{:?}", move_out));
1933 s.push_str("ever_init: [");
1934 let mut saw_one = false;
1935 self.ever_inits.each_state_bit(|mpi_ever_init| {
1936 if saw_one { s.push_str(", "); };
1939 &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init];
1940 s.push_str(&format!("{:?}", ever_init));
1948 impl<'b, 'gcx, 'tcx> FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>> {
1949 fn has_any_child_of(&self, mpi: MovePathIndex) -> Option<MovePathIndex> {
1950 let move_data = self.base_results.operator().move_data();
1952 let mut todo = vec![mpi];
1953 let mut push_siblings = false; // don't look at siblings of original `mpi`.
1954 while let Some(mpi) = todo.pop() {
1955 if self.curr_state.contains(&mpi) {
1958 let move_path = &move_data.move_paths[mpi];
1959 if let Some(child) = move_path.first_child {
1963 if let Some(sibling) = move_path.next_sibling {
1967 // after we've processed the original `mpi`, we should
1968 // always traverse the siblings of any of its
1970 push_siblings = true;
1977 impl<BD> FlowInProgress<BD> where BD: BitDenotation {
1978 fn each_state_bit<F>(&self, f: F) where F: FnMut(BD::Idx) {
1979 self.curr_state.each_bit(self.base_results.operator().bits_per_block(), f)
1982 fn each_gen_bit<F>(&self, f: F) where F: FnMut(BD::Idx) {
1983 self.stmt_gen.each_bit(self.base_results.operator().bits_per_block(), f)
1986 fn new(results: DataflowResults<BD>) -> Self {
1987 let bits_per_block = results.sets().bits_per_block();
1988 let curr_state = IdxSetBuf::new_empty(bits_per_block);
1989 let stmt_gen = IdxSetBuf::new_empty(bits_per_block);
1990 let stmt_kill = IdxSetBuf::new_empty(bits_per_block);
1992 base_results: results,
1993 curr_state: curr_state,
1995 stmt_kill: stmt_kill,
1999 fn reset_to_entry_of(&mut self, bb: BasicBlock) {
2000 (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index()));
2003 fn reconstruct_statement_effect(&mut self, loc: Location) {
2004 self.stmt_gen.reset_to_empty();
2005 self.stmt_kill.reset_to_empty();
2006 let mut ignored = IdxSetBuf::new_empty(0);
2007 let mut sets = BlockSets {
2008 on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill,
2010 self.base_results.operator().statement_effect(&mut sets, loc);
2013 fn reconstruct_terminator_effect(&mut self, loc: Location) {
2014 self.stmt_gen.reset_to_empty();
2015 self.stmt_kill.reset_to_empty();
2016 let mut ignored = IdxSetBuf::new_empty(0);
2017 let mut sets = BlockSets {
2018 on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill,
2020 self.base_results.operator().terminator_effect(&mut sets, loc);
2023 fn apply_local_effect(&mut self) {
2024 self.curr_state.union(&self.stmt_gen);
2025 self.curr_state.subtract(&self.stmt_kill);
2028 fn elems_incoming(&self) -> indexed_set::Elems<BD::Idx> {
2029 let univ = self.base_results.sets().bits_per_block();
2030 self.curr_state.elems(univ)