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 pass borrow-checks the MIR to (further) ensure it is not broken.
13 use rustc::infer::{InferCtxt};
14 use rustc::ty::{self, TyCtxt, ParamEnv};
15 use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Location, Lvalue};
16 use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
17 use rustc::mir::{Statement, StatementKind, Terminator, TerminatorKind};
18 use rustc::mir::transform::{MirPass, MirSource};
20 use rustc_data_structures::indexed_set::{self, IdxSetBuf};
21 use rustc_data_structures::indexed_vec::{Idx};
23 use syntax::ast::{self};
24 use syntax_pos::{DUMMY_SP, Span};
26 use dataflow::{do_dataflow};
27 use dataflow::{MoveDataParamEnv};
28 use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer};
29 use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
30 use dataflow::{Borrows, BorrowData, BorrowIndex};
31 use dataflow::move_paths::{HasMoveData, MoveData, MovePathIndex, LookupResult};
32 use util::borrowck_errors::{BorrowckErrors, Origin};
34 use self::MutateMode::{JustWrite, WriteAndRead};
35 use self::ConsumeKind::{Consume};
37 pub struct BorrowckMir;
39 impl MirPass for BorrowckMir {
40 fn run_pass<'a, 'tcx>(&self, tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &mut Mir<'tcx>) {
42 // let err_count = tcx.sess.err_count();
44 // // compiling a broken program can obviously result in a
45 // // broken MIR, so try not to report duplicate errors.
46 // debug!("skipping BorrowckMir: {} due to {} previous errors",
47 // tcx.node_path_str(src.item_id()), err_count);
51 debug!("run_pass BorrowckMir: {}", tcx.node_path_str(src.item_id()));
53 let def_id = tcx.hir.local_def_id(src.item_id());
54 if tcx.has_attr(def_id, "rustc_mir_borrowck") || tcx.sess.opts.debugging_opts.borrowck_mir {
55 borrowck_mir(tcx, src, mir);
60 fn borrowck_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, src: MirSource, mir: &Mir<'tcx>)
62 let id = src.item_id();
63 let def_id = tcx.hir.local_def_id(id);
64 debug!("borrowck_mir({}) UNIMPLEMENTED", tcx.item_path_str(def_id));
66 let attributes = tcx.get_attrs(def_id);
67 let param_env = tcx.param_env(def_id);
68 tcx.infer_ctxt().enter(|_infcx| {
70 let move_data = MoveData::gather_moves(mir, tcx, param_env);
71 let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env };
72 let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
73 let flow_borrows = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
74 Borrows::new(tcx, mir),
75 |bd, i| bd.location(i));
76 let flow_inits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
77 MaybeInitializedLvals::new(tcx, mir, &mdpe),
78 |bd, i| &bd.move_data().move_paths[i]);
79 let flow_uninits = do_dataflow(tcx, mir, id, &attributes, &dead_unwinds,
80 MaybeUninitializedLvals::new(tcx, mir, &mdpe),
81 |bd, i| &bd.move_data().move_paths[i]);
83 let mut mbcx = MirBorrowckCtxt {
87 move_data: &mdpe.move_data,
89 fake_infer_ctxt: &_infcx,
92 let mut state = InProgress::new(flow_borrows,
96 mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
99 debug!("borrowck_mir done");
103 pub struct MirBorrowckCtxt<'c, 'b, 'a: 'b+'c, 'gcx: 'a+'tcx, 'tcx: 'a> {
104 tcx: TyCtxt<'a, 'gcx, 'gcx>,
106 node_id: ast::NodeId,
107 move_data: &'b MoveData<'gcx>,
108 param_env: ParamEnv<'tcx>,
109 fake_infer_ctxt: &'c InferCtxt<'c, 'gcx, 'tcx>,
112 // (forced to be `pub` due to its use as an associated type below.)
113 pub struct InProgress<'b, 'tcx: 'b> {
114 borrows: FlowInProgress<Borrows<'b, 'tcx>>,
115 inits: FlowInProgress<MaybeInitializedLvals<'b, 'tcx>>,
116 uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>>,
119 struct FlowInProgress<BD> where BD: BitDenotation {
120 base_results: DataflowResults<BD>,
121 curr_state: IdxSetBuf<BD::Idx>,
122 stmt_gen: IdxSetBuf<BD::Idx>,
123 stmt_kill: IdxSetBuf<BD::Idx>,
127 // 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
128 // 2. loans made in overlapping scopes do not conflict
129 // 3. assignments do not affect things loaned out as immutable
130 // 4. moves do not affect things loaned out in any way
131 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> DataflowResultsConsumer<'b, 'gcx>
132 for MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx>
134 type FlowState = InProgress<'b, 'gcx>;
136 fn mir(&self) -> &'b Mir<'gcx> { self.mir }
138 fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
139 flow_state.each_flow(|b| b.reset_to_entry_of(bb),
140 |i| i.reset_to_entry_of(bb),
141 |u| u.reset_to_entry_of(bb));
144 fn reconstruct_statement_effect(&mut self,
146 flow_state: &mut Self::FlowState) {
147 flow_state.each_flow(|b| b.reconstruct_statement_effect(location),
148 |i| i.reconstruct_statement_effect(location),
149 |u| u.reconstruct_statement_effect(location));
152 fn apply_local_effect(&mut self,
154 flow_state: &mut Self::FlowState) {
155 flow_state.each_flow(|b| b.apply_local_effect(),
156 |i| i.apply_local_effect(),
157 |u| u.apply_local_effect());
160 fn reconstruct_terminator_effect(&mut self,
162 flow_state: &mut Self::FlowState) {
163 flow_state.each_flow(|b| b.reconstruct_terminator_effect(location),
164 |i| i.reconstruct_terminator_effect(location),
165 |u| u.reconstruct_terminator_effect(location));
168 fn visit_block_entry(&mut self,
170 flow_state: &Self::FlowState) {
171 let summary = flow_state.summary();
172 debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary);
175 fn visit_statement_entry(&mut self,
177 stmt: &Statement<'gcx>,
178 flow_state: &Self::FlowState) {
179 let summary = flow_state.summary();
180 debug!("MirBorrowckCtxt::process_statement({:?}, {:?}): {}", location, stmt, summary);
181 let span = stmt.source_info.span;
183 StatementKind::Assign(ref lhs, ref rhs) => {
184 self.mutate_lvalue(ContextKind::AssignLhs.new(location),
185 (lhs, span), JustWrite, flow_state);
186 self.consume_rvalue(ContextKind::AssignRhs.new(location),
187 (rhs, span), location, flow_state);
189 StatementKind::SetDiscriminant { ref lvalue, variant_index: _ } => {
190 self.mutate_lvalue(ContextKind::SetDiscrim.new(location),
191 (lvalue, span), JustWrite, flow_state);
193 StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => {
194 for (o, output) in asm.outputs.iter().zip(outputs) {
196 self.consume_lvalue(ContextKind::InlineAsm.new(location),
201 self.mutate_lvalue(ContextKind::InlineAsm.new(location),
203 if o.is_rw { WriteAndRead } else { JustWrite },
207 for input in inputs {
208 self.consume_operand(ContextKind::InlineAsm.new(location),
210 (input, span), flow_state);
213 StatementKind::EndRegion(ref _rgn) => {
214 // ignored when consuming results (update to
215 // flow_state already handled).
218 StatementKind::Validate(..) |
219 StatementKind::StorageLive(..) => {
220 // ignored by borrowck
223 StatementKind::StorageDead(ref lvalue) => {
224 // causes non-drop values to be dropped.
225 self.consume_lvalue(ContextKind::StorageDead.new(location),
226 ConsumeKind::Consume,
233 fn visit_terminator_entry(&mut self,
235 term: &Terminator<'gcx>,
236 flow_state: &Self::FlowState) {
238 let summary = flow_state.summary();
239 debug!("MirBorrowckCtxt::process_terminator({:?}, {:?}): {}", location, term, summary);
240 let span = term.source_info.span;
242 TerminatorKind::SwitchInt { ref discr, switch_ty: _, values: _, targets: _ } => {
243 self.consume_operand(ContextKind::SwitchInt.new(loc),
245 (discr, span), flow_state);
247 TerminatorKind::Drop { location: ref drop_lvalue, target: _, unwind: _ } => {
248 self.consume_lvalue(ContextKind::Drop.new(loc),
250 (drop_lvalue, span), flow_state);
252 TerminatorKind::DropAndReplace { location: ref drop_lvalue,
253 value: ref new_value,
256 self.mutate_lvalue(ContextKind::DropAndReplace.new(loc),
257 (drop_lvalue, span), JustWrite, flow_state);
258 self.consume_operand(ContextKind::DropAndReplace.new(loc),
260 (new_value, span), flow_state);
262 TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => {
263 self.consume_operand(ContextKind::CallOperator.new(loc),
265 (func, span), flow_state);
267 self.consume_operand(ContextKind::CallOperand.new(loc),
269 (arg, span), flow_state);
271 if let Some((ref dest, _/*bb*/)) = *destination {
272 self.mutate_lvalue(ContextKind::CallDest.new(loc),
273 (dest, span), JustWrite, flow_state);
276 TerminatorKind::Assert { ref cond, expected: _, ref msg, target: _, cleanup: _ } => {
277 self.consume_operand(ContextKind::Assert.new(loc),
279 (cond, span), flow_state);
281 AssertMessage::BoundsCheck { ref len, ref index } => {
282 self.consume_operand(ContextKind::Assert.new(loc),
284 (len, span), flow_state);
285 self.consume_operand(ContextKind::Assert.new(loc),
287 (index, span), flow_state);
289 AssertMessage::Math(_/*const_math_err*/) => {}
293 TerminatorKind::Goto { target: _ } |
294 TerminatorKind::Resume |
295 TerminatorKind::Return |
296 TerminatorKind::Unreachable => {
297 // no data used, thus irrelevant to borrowck
303 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
304 enum MutateMode { JustWrite, WriteAndRead }
306 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
307 enum ConsumeKind { Drop, Consume }
309 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
310 enum Control { Continue, Break }
312 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
313 fn mutate_lvalue(&mut self,
315 lvalue_span: (&Lvalue<'gcx>, Span),
317 flow_state: &InProgress<'b, 'gcx>) {
318 // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
320 MutateMode::WriteAndRead => {
321 self.check_if_path_is_moved(context, lvalue_span, flow_state);
323 MutateMode::JustWrite => {
324 self.check_if_assigned_path_is_moved(context, lvalue_span, flow_state);
328 // check we don't invalidate any outstanding loans
329 self.each_borrow_involving_path(context,
330 lvalue_span.0, flow_state, |this, _index, _data| {
331 this.report_illegal_mutation_of_borrowed(context,
336 // check for reassignments to immutable local variables
337 self.check_if_reassignment_to_immutable_state(context, lvalue_span, flow_state);
340 fn consume_rvalue(&mut self,
342 (rvalue, span): (&Rvalue<'gcx>, Span),
344 flow_state: &InProgress<'b, 'gcx>) {
346 Rvalue::Ref(_/*rgn*/, bk, ref lvalue) => {
347 self.borrow(context, location, bk, (lvalue, span), flow_state)
350 Rvalue::Use(ref operand) |
351 Rvalue::Repeat(ref operand, _) |
352 Rvalue::UnaryOp(_/*un_op*/, ref operand) |
353 Rvalue::Cast(_/*cast_kind*/, ref operand, _/*ty*/) => {
354 self.consume_operand(context, Consume, (operand, span), flow_state)
357 Rvalue::Len(ref lvalue) |
358 Rvalue::Discriminant(ref lvalue) => {
359 // len(_)/discriminant(_) merely read, not consume.
360 self.check_if_path_is_moved(context, (lvalue, span), flow_state);
363 Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) |
364 Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => {
365 self.consume_operand(context, Consume, (operand1, span), flow_state);
366 self.consume_operand(context, Consume, (operand2, span), flow_state);
369 Rvalue::NullaryOp(_op, _ty) => {
370 // nullary ops take no dynamic input; no borrowck effect.
372 // FIXME: is above actually true? Do we want to track
373 // the fact that uninitialized data can be created via
377 Rvalue::Aggregate(ref _aggregate_kind, ref operands) => {
378 for operand in operands {
379 self.consume_operand(context, Consume, (operand, span), flow_state);
385 fn consume_operand(&mut self,
387 consume_via_drop: ConsumeKind,
388 (operand, span): (&Operand<'gcx>, Span),
389 flow_state: &InProgress<'b, 'gcx>) {
391 Operand::Consume(ref lvalue) =>
392 self.consume_lvalue(context, consume_via_drop, (lvalue, span), flow_state),
393 Operand::Constant(_) => {}
397 fn consume_lvalue(&mut self,
399 consume_via_drop: ConsumeKind,
400 lvalue_span: (&Lvalue<'gcx>, Span),
401 flow_state: &InProgress<'b, 'gcx>) {
402 let lvalue = lvalue_span.0;
403 let ty = lvalue.ty(self.mir, self.tcx).to_ty(self.tcx);
404 let moves_by_default =
405 self.fake_infer_ctxt.type_moves_by_default(self.param_env, ty, DUMMY_SP);
406 if moves_by_default {
407 // move of lvalue: check if this is move of already borrowed path
408 self.each_borrow_involving_path(
409 context, lvalue_span.0, flow_state, |this, _idx, borrow| {
410 if !borrow.compatible_with(BorrowKind::Mut) {
411 this.report_move_out_while_borrowed(context, lvalue_span);
418 // copy of lvalue: check if this is "copy of frozen path" (FIXME: see check_loans.rs)
419 self.each_borrow_involving_path(
420 context, lvalue_span.0, flow_state, |this, _idx, borrow| {
421 if !borrow.compatible_with(BorrowKind::Shared) {
422 this.report_use_while_mutably_borrowed(context, lvalue_span);
430 // Finally, check if path was already moved.
431 match consume_via_drop {
432 ConsumeKind::Drop => {
433 // If path is merely being dropped, then we'll already
434 // check the drop flag to see if it is moved (thus we
435 // skip this check in that case).
437 ConsumeKind::Consume => {
438 self.check_if_path_is_moved(context, lvalue_span, flow_state);
447 lvalue_span: (&Lvalue<'gcx>, Span),
448 flow_state: &InProgress<'b, 'gcx>) {
449 debug!("borrow location: {:?} lvalue: {:?} span: {:?}",
450 location, lvalue_span.0, lvalue_span.1);
451 self.check_if_path_is_moved(context, lvalue_span, flow_state);
452 self.check_for_conflicting_loans(context, location, bk, lvalue_span, flow_state);
456 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
457 fn check_if_reassignment_to_immutable_state(&mut self,
459 (lvalue, span): (&Lvalue<'gcx>, Span),
460 flow_state: &InProgress<'b, 'gcx>) {
461 let move_data = flow_state.inits.base_results.operator().move_data();
463 // determine if this path has a non-mut owner (and thus needs checking).
467 Lvalue::Projection(ref proj) => {
471 Lvalue::Local(local) => {
472 match self.mir.local_decls[local].mutability {
473 Mutability::Not => break, // needs check
474 Mutability::Mut => return,
477 Lvalue::Static(_) => {
478 // mutation of non-mut static is always illegal,
479 // independent of dataflow.
480 self.report_assignment_to_static(context, (lvalue, span));
486 if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) {
487 if flow_state.inits.curr_state.contains(&mpi) {
488 // may already be assigned before reaching this statement;
490 self.report_illegal_reassignment(context, (lvalue, span));
495 fn check_if_path_is_moved(&mut self,
497 lvalue_span: (&Lvalue<'gcx>, Span),
498 flow_state: &InProgress<'b, 'gcx>) {
499 // FIXME: analogous code in check_loans first maps `lvalue` to
500 // its base_path ... but is that what we want here?
501 let lvalue = self.base_path(lvalue_span.0);
503 let maybe_uninits = &flow_state.uninits;
504 let move_data = maybe_uninits.base_results.operator().move_data();
505 if let Some(mpi) = self.move_path_for_lvalue(context, move_data, lvalue) {
506 if maybe_uninits.curr_state.contains(&mpi) {
507 // find and report move(s) that could cause this to be uninitialized
508 self.report_use_of_moved(context, lvalue_span);
510 // sanity check: initialized on *some* path, right?
511 assert!(flow_state.inits.curr_state.contains(&mpi));
516 fn move_path_for_lvalue(&mut self,
518 move_data: &MoveData<'gcx>,
519 lvalue: &Lvalue<'gcx>)
520 -> Option<MovePathIndex>
522 // If returns None, then there is no move path corresponding
523 // to a direct owner of `lvalue` (which means there is nothing
524 // that borrowck tracks for its analysis).
526 match move_data.rev_lookup.find(lvalue) {
527 LookupResult::Parent(_) => None,
528 LookupResult::Exact(mpi) => Some(mpi),
532 fn check_if_assigned_path_is_moved(&mut self,
534 (lvalue, span): (&Lvalue<'gcx>, Span),
535 flow_state: &InProgress<'b, 'gcx>) {
536 // recur down lvalue; dispatch to check_if_path_is_moved when necessary
537 let mut lvalue = lvalue;
540 Lvalue::Local(_) | Lvalue::Static(_) => {
541 // assigning to `x` does not require `x` be initialized.
544 Lvalue::Projection(ref proj) => {
545 let Projection { ref base, ref elem } = **proj;
547 ProjectionElem::Deref |
548 // assigning to *P requires `P` initialized.
549 ProjectionElem::Index(_/*operand*/) |
550 ProjectionElem::ConstantIndex { .. } |
551 // assigning to P[i] requires `P` initialized.
552 ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
553 // assigning to (P->variant) is okay if assigning to `P` is okay
555 // FIXME: is this true even if P is a adt with a dtor?
558 ProjectionElem::Subslice { .. } => {
559 panic!("we dont allow assignments to subslices, context: {:?}",
563 ProjectionElem::Field(..) => {
564 // if type of `P` has a dtor, then
565 // assigning to `P.f` requires `P` itself
566 // be already initialized
568 match base.ty(self.mir, tcx).to_ty(tcx).sty {
569 ty::TyAdt(def, _) if def.has_dtor(tcx) => {
571 // FIXME: analogous code in
572 // check_loans.rs first maps
573 // `base` to its base_path.
575 self.check_if_path_is_moved(context,
576 (base, span), flow_state);
578 // (base initialized; no need to
594 fn check_for_conflicting_loans(&mut self,
598 lvalue_span: (&Lvalue<'gcx>, Span),
599 flow_state: &InProgress<'b, 'gcx>) {
600 // NOTE FIXME: The analogous code in old borrowck
601 // check_loans.rs is careful to iterate over every *issued*
602 // loan, as opposed to just the in scope ones.
604 // (Or if you prefer, all the *other* iterations over loans
605 // only consider loans that are in scope of some given
608 // The (currently skeletal) code here does not encode such a
609 // distinction, which means it is almost certainly over
610 // looking something.
612 // (It is probably going to reject code that should be
613 // accepted, I suspect, by treated issued-but-out-of-scope
614 // loans as issued-and-in-scope, and thus causing them to
615 // interfere with other loans.)
617 // However, I just want to get something running, especially
618 // since I am trying to move into new territory with NLL, so
619 // lets get this going first, and then address the issued vs
620 // in-scope distinction later.
622 let state = &flow_state.borrows;
623 let data = &state.base_results.operator().borrows();
625 debug!("check_for_conflicting_loans location: {:?}", _location);
627 // does any loan generated here conflict with a previously issued loan?
628 let mut loans_generated = 0;
629 for (g, gen) in state.elems_generated().map(|g| (g, &data[g])) {
630 loans_generated += 1;
631 for (i, issued) in state.elems_incoming().map(|i| (i, &data[i])) {
632 debug!("check_for_conflicting_loans gen: {:?} issued: {:?} conflicts: {}",
633 (g, gen, self.base_path(&gen.lvalue),
634 self.restrictions(&gen.lvalue).collect::<Vec<_>>()),
635 (i, issued, self.base_path(&issued.lvalue),
636 self.restrictions(&issued.lvalue).collect::<Vec<_>>()),
637 self.conflicts_with(gen, issued));
638 if self.conflicts_with(gen, issued) {
639 self.report_conflicting_borrow(context, lvalue_span, gen, issued);
644 // MIR statically ensures each statement gens *at most one*
645 // loan; mutual conflict (within a statement) can't arise.
647 // As safe-guard, assert that above property actually holds.
648 assert!(loans_generated <= 1);
651 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
652 fn each_borrow_involving_path<F>(&mut self,
654 lvalue: &Lvalue<'gcx>,
655 flow_state: &InProgress<'b, 'gcx>,
657 where F: FnMut(&mut Self, BorrowIndex, &BorrowData<'gcx>) -> Control
659 // FIXME: analogous code in check_loans first maps `lvalue` to
662 let domain = flow_state.borrows.base_results.operator();
663 let data = domain.borrows();
665 // check for loan restricting path P being used. Accounts for
666 // borrows of P, P.a.b, etc.
667 for i in flow_state.borrows.elems_incoming() {
668 // FIXME: check_loans.rs filtered this to "in scope"
669 // loans; i.e. it took a scope S and checked that each
670 // restriction's kill_scope was a superscope of S.
671 let borrowed = &data[i];
672 for restricted in self.restrictions(&borrowed.lvalue) {
673 if restricted == lvalue {
674 let ctrl = op(self, i, borrowed);
675 if ctrl == Control::Break { return; }
680 // check for loans (not restrictions) on any base path.
681 // e.g. Rejects `{ let x = &mut a.b; let y = a.b.c; }`,
682 // since that moves out of borrowed path `a.b`.
684 // Limiting to loans (not restrictions) keeps this one
685 // working: `{ let x = &mut a.b; let y = a.c; }`
686 let mut cursor = lvalue;
688 // FIXME: check_loans.rs invoked `op` *before* cursor
689 // shift here. Might just work (and even avoid redundant
690 // errors?) given code above? But for now, I want to try
691 // doing what I think is more "natural" check.
692 for i in flow_state.borrows.elems_incoming() {
693 let borrowed = &data[i];
694 if borrowed.lvalue == *cursor {
695 let ctrl = op(self, i, borrowed);
696 if ctrl == Control::Break { return; }
701 Lvalue::Local(_) | Lvalue::Static(_) => break,
702 Lvalue::Projection(ref proj) => cursor = &proj.base,
709 use super::MirBorrowckCtxt;
712 use rustc::ty::{self, TyCtxt};
713 use rustc::mir::{Lvalue, Mir, Operand, ProjectionElem};
715 pub(super) struct Restrictions<'c, 'tcx: 'c> {
717 tcx: TyCtxt<'c, 'tcx, 'tcx>,
718 lvalue_stack: Vec<&'c Lvalue<'tcx>>,
721 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
722 pub(super) fn restrictions<'d>(&self,
723 lvalue: &'d Lvalue<'gcx>)
724 -> Restrictions<'d, 'gcx> where 'b: 'd
726 let lvalue_stack = if self.has_restrictions(lvalue) { vec![lvalue] } else { vec![] };
727 Restrictions { lvalue_stack: lvalue_stack, mir: self.mir, tcx: self.tcx }
730 fn has_restrictions(&self, lvalue: &Lvalue<'gcx>) -> bool {
731 let mut cursor = lvalue;
733 let proj = match *cursor {
734 Lvalue::Local(_) => return true,
735 Lvalue::Static(_) => return false,
736 Lvalue::Projection(ref proj) => proj,
739 ProjectionElem::Index(..) |
740 ProjectionElem::ConstantIndex { .. } |
741 ProjectionElem::Downcast(..) |
742 ProjectionElem::Subslice { .. } |
743 ProjectionElem::Field(_/*field*/, _/*ty*/) => {
747 ProjectionElem::Deref => {
748 let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
753 ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
754 // FIXME: do I need to check validity of
755 // region here though? (I think the original
756 // check_loans code did, like readme says)
759 ty::TyRef(_, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
763 ty::TyAdt(..) if ty.is_box() => {
768 panic!("unknown type fed to Projection Deref.");
777 impl<'c, 'tcx> Iterator for Restrictions<'c, 'tcx> {
778 type Item = &'c Lvalue<'tcx>;
779 fn next(&mut self) -> Option<Self::Item> {
781 let lvalue = match self.lvalue_stack.pop() {
783 Some(lvalue) => lvalue,
786 // `lvalue` may not be a restriction itself, but may
787 // hold one further down (e.g. we never return
788 // downcasts here, but may return a base of a
791 // Also, we need to enqueue any additional
792 // subrestrictions that it implies, since we can only
793 // return from from this call alone.
795 let mut cursor = lvalue;
797 let proj = match *cursor {
798 Lvalue::Local(_) => return Some(cursor), // search yielded this leaf
799 Lvalue::Static(_) => continue 'pop, // fruitless leaf; try next on stack
800 Lvalue::Projection(ref proj) => proj,
804 ProjectionElem::Field(_/*field*/, _/*ty*/) => {
805 // FIXME: add union handling
806 self.lvalue_stack.push(&proj.base);
809 ProjectionElem::Downcast(..) |
810 ProjectionElem::Subslice { .. } |
811 ProjectionElem::ConstantIndex { .. } |
812 ProjectionElem::Index(Operand::Constant(..)) => {
816 ProjectionElem::Index(Operand::Consume(ref index)) => {
817 self.lvalue_stack.push(index); // FIXME: did old borrowck do this?
821 ProjectionElem::Deref => {
826 assert_eq!(proj.elem, ProjectionElem::Deref);
828 let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
831 // borrowck ignores raw ptrs; treat analogous to imm borrow
834 // R-Deref-Imm-Borrowed
835 ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => {
836 // immutably-borrowed referents do not
837 // have recursively-implied restrictions
838 // (because preventing actions on `*LV`
839 // does nothing about aliases like `*LV1`)
841 // FIXME: do I need to check validity of
842 // `_r` here though? (I think the original
843 // check_loans code did, like the readme
846 // (And do I *really* not have to
847 // recursively process the `base` as a
848 // further search here? Leaving this `if
849 // false` here as a hint to look at this
852 // Ah, it might be because the
853 // restrictions are distinct from the path
854 // substructure. Note that there is a
855 // separate loop over the path
856 // substructure in fn
857 // each_borrow_involving_path, for better
868 // R-Deref-Mut-Borrowed
869 ty::TyRef(_/*rgn*/, ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => {
870 // mutably-borrowed referents are
871 // themselves restricted.
873 // FIXME: do I need to check validity of
874 // `_r` here though? (I think the original
875 // check_loans code did, like the readme
878 // schedule base for future iteration.
879 self.lvalue_stack.push(&proj.base);
880 return Some(cursor); // search yielded interior node
883 // R-Deref-Send-Pointer
884 ty::TyAdt(..) if ty.is_box() => {
885 // borrowing interior of a box implies that
886 // its base can no longer be mutated (o/w box
887 // storage would be freed)
888 self.lvalue_stack.push(&proj.base);
889 return Some(cursor); // search yielded interior node
892 _ => panic!("unknown type fed to Projection Deref."),
900 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
901 fn report_use_of_moved(&mut self,
903 (lvalue, span): (&Lvalue, Span)) {
904 let mut err = self.tcx.cannot_act_on_uninitialized_variable(
905 span, "use", &self.describe_lvalue(lvalue), Origin::Mir);
906 // FIXME: add span_label for use of uninitialized variable
910 fn report_move_out_while_borrowed(&mut self,
912 (lvalue, span): (&Lvalue, Span)) {
913 let mut err = self.tcx.cannot_move_when_borrowed(
914 span, &self.describe_lvalue(lvalue), Origin::Mir);
915 // FIXME 1: add span_label for "borrow of `()` occurs here"
916 // FIXME 2: add span_label for "move out of `{}` occurs here"
920 fn report_use_while_mutably_borrowed(&mut self,
922 (lvalue, span): (&Lvalue, Span)) {
923 let mut err = self.tcx.cannot_use_when_mutably_borrowed(
924 span, &self.describe_lvalue(lvalue), Origin::Mir);
925 // FIXME 1: add span_label for "borrow of `()` occurs here"
926 // FIXME 2: add span_label for "use of `{}` occurs here"
930 fn report_conflicting_borrow(&mut self,
932 (lvalue, span): (&Lvalue, Span),
934 loan2: &BorrowData) {
935 // FIXME: obviously falsifiable. Generalize for non-eq lvalues later.
936 assert_eq!(loan1.lvalue, loan2.lvalue);
938 // FIXME: supply non-"" `opt_via` when appropriate
939 let mut err = match (loan1.kind, "immutable", "mutable",
940 loan2.kind, "immutable", "mutable") {
941 (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
942 (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) |
943 (BorrowKind::Mut, _, lft, BorrowKind::Mut, _, rgt) =>
944 self.tcx.cannot_reborrow_already_borrowed(
945 span, &self.describe_lvalue(lvalue),
946 "", lft, "it", rgt, "", Origin::Mir),
948 _ => self.tcx.cannot_mutably_borrow_multiply(
949 span, &self.describe_lvalue(lvalue), "", Origin::Mir),
950 // FIXME: add span labels for first and second mutable borrows, as well as
951 // end point for first.
956 fn report_illegal_mutation_of_borrowed(&mut self, _: Context, (lvalue, span): (&Lvalue, Span)) {
957 let mut err = self.tcx.cannot_assign_to_borrowed(
958 span, &self.describe_lvalue(lvalue), Origin::Mir);
959 // FIXME: add span labels for borrow and assignment points
963 fn report_illegal_reassignment(&mut self, _context: Context, (lvalue, span): (&Lvalue, Span)) {
964 let mut err = self.tcx.cannot_reassign_immutable(
965 span, &self.describe_lvalue(lvalue), Origin::Mir);
966 // FIXME: add span labels for borrow and assignment points
970 fn report_assignment_to_static(&mut self, _context: Context, (lvalue, span): (&Lvalue, Span)) {
971 let mut err = self.tcx.cannot_assign_static(
972 span, &self.describe_lvalue(lvalue), Origin::Mir);
973 // FIXME: add span labels for borrow and assignment points
978 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
979 // End-user visible description of `lvalue`
980 fn describe_lvalue(&self, lvalue: &Lvalue) -> String {
981 let mut buf = String::new();
982 self.append_lvalue_to_string(lvalue, &mut buf);
986 // Appends end-user visible description of `lvalue` to `buf`.
987 fn append_lvalue_to_string(&self, lvalue: &Lvalue, buf: &mut String) {
989 Lvalue::Local(local) => {
990 let local = &self.mir.local_decls[local];
992 Some(name) => buf.push_str(&format!("{}", name)),
993 None => buf.push_str("_"),
996 Lvalue::Static(ref static_) => {
997 buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
999 Lvalue::Projection(ref proj) => {
1000 let (prefix, suffix, index_operand) = match proj.elem {
1001 ProjectionElem::Deref =>
1002 ("(*", format!(")"), None),
1003 ProjectionElem::Downcast(..) =>
1004 ("", format!(""), None), // (dont emit downcast info)
1005 ProjectionElem::Field(field, _ty) =>
1006 ("", format!(".{}", field.index()), None),
1007 ProjectionElem::Index(ref index) =>
1008 ("", format!(""), Some(index)),
1009 ProjectionElem::ConstantIndex { offset, min_length, from_end: true } =>
1010 ("", format!("[{} of {}]", offset, min_length), None),
1011 ProjectionElem::ConstantIndex { offset, min_length, from_end: false } =>
1012 ("", format!("[-{} of {}]", offset, min_length), None),
1013 ProjectionElem::Subslice { from, to: 0 } =>
1014 ("", format!("[{}:]", from), None),
1015 ProjectionElem::Subslice { from: 0, to } =>
1016 ("", format!("[:-{}]", to), None),
1017 ProjectionElem::Subslice { from, to } =>
1018 ("", format!("[{}:-{}]", from, to), None),
1020 buf.push_str(prefix);
1021 self.append_lvalue_to_string(&proj.base, buf);
1022 if let Some(index) = index_operand {
1024 self.append_operand_to_string(index, buf);
1027 buf.push_str(&suffix);
1034 fn append_operand_to_string(&self, operand: &Operand, buf: &mut String) {
1036 Operand::Consume(ref lvalue) => {
1037 self.append_lvalue_to_string(lvalue, buf);
1039 Operand::Constant(ref constant) => {
1040 buf.push_str(&format!("{:?}", constant));
1046 impl<'c, 'b, 'a: 'b+'c, 'gcx, 'tcx: 'a> MirBorrowckCtxt<'c, 'b, 'a, 'gcx, 'tcx> {
1047 // FIXME: needs to be able to express errors analogous to check_loans.rs
1048 fn conflicts_with(&self, loan1: &BorrowData<'gcx>, loan2: &BorrowData<'gcx>) -> bool {
1049 if loan1.compatible_with(loan2.kind) { return false; }
1051 let loan2_base_path = self.base_path(&loan2.lvalue);
1052 for restricted in self.restrictions(&loan1.lvalue) {
1053 if restricted != loan2_base_path { continue; }
1057 let loan1_base_path = self.base_path(&loan1.lvalue);
1058 for restricted in self.restrictions(&loan2.lvalue) {
1059 if restricted != loan1_base_path { continue; }
1066 // FIXME (#16118): function intended to allow the borrow checker
1067 // to be less precise in its handling of Box while still allowing
1068 // moves out of a Box. They should be removed when/if we stop
1069 // treating Box specially (e.g. when/if DerefMove is added...)
1071 fn base_path<'d>(&self, lvalue: &'d Lvalue<'gcx>) -> &'d Lvalue<'gcx> {
1072 //! Returns the base of the leftmost (deepest) dereference of an
1073 //! Box in `lvalue`. If there is no dereference of an Box
1074 //! in `lvalue`, then it just returns `lvalue` itself.
1076 let mut cursor = lvalue;
1077 let mut deepest = lvalue;
1079 let proj = match *cursor {
1080 Lvalue::Local(..) | Lvalue::Static(..) => return deepest,
1081 Lvalue::Projection(ref proj) => proj,
1083 if proj.elem == ProjectionElem::Deref &&
1084 lvalue.ty(self.mir, self.tcx).to_ty(self.tcx).is_box()
1086 deepest = &proj.base;
1088 cursor = &proj.base;
1093 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1099 #[derive(Copy, Clone, PartialEq, Eq, Debug)]
1116 fn new(self, loc: Location) -> Context { Context { kind: self, loc: loc } }
1119 impl<'b, 'tcx: 'b> InProgress<'b, 'tcx> {
1120 pub(super) fn new(borrows: DataflowResults<Borrows<'b, 'tcx>>,
1121 inits: DataflowResults<MaybeInitializedLvals<'b, 'tcx>>,
1122 uninits: DataflowResults<MaybeUninitializedLvals<'b, 'tcx>>)
1125 borrows: FlowInProgress::new(borrows),
1126 inits: FlowInProgress::new(inits),
1127 uninits: FlowInProgress::new(uninits),
1131 fn each_flow<XB, XI, XU>(&mut self,
1132 mut xform_borrows: XB,
1133 mut xform_inits: XI,
1134 mut xform_uninits: XU) where
1135 XB: FnMut(&mut FlowInProgress<Borrows<'b, 'tcx>>),
1136 XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'tcx>>),
1137 XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'tcx>>),
1139 xform_borrows(&mut self.borrows);
1140 xform_inits(&mut self.inits);
1141 xform_uninits(&mut self.uninits);
1144 fn summary(&self) -> String {
1145 let mut s = String::new();
1147 s.push_str("borrows in effect: [");
1148 let mut saw_one = false;
1149 self.borrows.each_state_bit(|borrow| {
1150 if saw_one { s.push_str(", "); };
1152 let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
1153 s.push_str(&format!("{}", borrow_data));
1157 s.push_str("borrows generated: [");
1158 let mut saw_one = false;
1159 self.borrows.each_gen_bit(|borrow| {
1160 if saw_one { s.push_str(", "); };
1162 let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
1163 s.push_str(&format!("{}", borrow_data));
1167 s.push_str("inits: [");
1168 let mut saw_one = false;
1169 self.inits.each_state_bit(|mpi_init| {
1170 if saw_one { s.push_str(", "); };
1173 &self.inits.base_results.operator().move_data().move_paths[mpi_init];
1174 s.push_str(&format!("{}", move_path));
1178 s.push_str("uninits: [");
1179 let mut saw_one = false;
1180 self.uninits.each_state_bit(|mpi_uninit| {
1181 if saw_one { s.push_str(", "); };
1184 &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit];
1185 s.push_str(&format!("{}", move_path));
1193 impl<BD> FlowInProgress<BD> where BD: BitDenotation {
1194 fn each_state_bit<F>(&self, f: F) where F: FnMut(BD::Idx) {
1195 self.curr_state.each_bit(self.base_results.operator().bits_per_block(), f)
1198 fn each_gen_bit<F>(&self, f: F) where F: FnMut(BD::Idx) {
1199 self.stmt_gen.each_bit(self.base_results.operator().bits_per_block(), f)
1202 fn new(results: DataflowResults<BD>) -> Self {
1203 let bits_per_block = results.sets().bits_per_block();
1204 let curr_state = IdxSetBuf::new_empty(bits_per_block);
1205 let stmt_gen = IdxSetBuf::new_empty(bits_per_block);
1206 let stmt_kill = IdxSetBuf::new_empty(bits_per_block);
1208 base_results: results,
1209 curr_state: curr_state,
1211 stmt_kill: stmt_kill,
1215 fn reset_to_entry_of(&mut self, bb: BasicBlock) {
1216 (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index()));
1219 fn reconstruct_statement_effect(&mut self, loc: Location) {
1220 self.stmt_gen.reset_to_empty();
1221 self.stmt_kill.reset_to_empty();
1222 let mut ignored = IdxSetBuf::new_empty(0);
1223 let mut sets = BlockSets {
1224 on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill,
1226 self.base_results.operator().statement_effect(&mut sets, loc);
1229 fn reconstruct_terminator_effect(&mut self, loc: Location) {
1230 self.stmt_gen.reset_to_empty();
1231 self.stmt_kill.reset_to_empty();
1232 let mut ignored = IdxSetBuf::new_empty(0);
1233 let mut sets = BlockSets {
1234 on_entry: &mut ignored, gen_set: &mut self.stmt_gen, kill_set: &mut self.stmt_kill,
1236 self.base_results.operator().terminator_effect(&mut sets, loc);
1239 fn apply_local_effect(&mut self) {
1240 self.curr_state.union(&self.stmt_gen);
1241 self.curr_state.subtract(&self.stmt_kill);
1244 fn elems_generated(&self) -> indexed_set::Elems<BD::Idx> {
1245 let univ = self.base_results.sets().bits_per_block();
1246 self.stmt_gen.elems(univ)
1249 fn elems_incoming(&self) -> indexed_set::Elems<BD::Idx> {
1250 let univ = self.base_results.sets().bits_per_block();
1251 self.curr_state.elems(univ)
1255 impl<'tcx> BorrowData<'tcx> {
1256 fn compatible_with(&self, bk: BorrowKind) -> bool {
1257 match (self.kind, bk) {
1258 (BorrowKind::Shared, BorrowKind::Shared) => true,
1260 (BorrowKind::Mut, _) |
1261 (BorrowKind::Unique, _) |
1262 (_, BorrowKind::Mut) |
1263 (_, BorrowKind::Unique) => false,