]> git.lizzy.rs Git - rust.git/commitdiff
inform constraint generation using maybe-init
authorPaul Daniel Faria <nashenas88@users.noreply.github.com>
Fri, 17 Nov 2017 09:34:02 +0000 (04:34 -0500)
committerNiko Matsakis <niko@alum.mit.edu>
Mon, 4 Dec 2017 13:51:11 +0000 (08:51 -0500)
In particular, if we see a variable is DROP-LIVE, but it is not
MAYBE-INIT, then we can ignore the drop. This leavess attempt to use
more complex refinements of the idea (e.g., for subpaths or subfields)
to future work.

27 files changed:
src/librustc_mir/borrow_check.rs [deleted file]
src/librustc_mir/borrow_check/mod.rs [new file with mode: 0644]
src/librustc_mir/borrow_check/nll/constraint_generation.rs [new file with mode: 0644]
src/librustc_mir/borrow_check/nll/free_regions.rs [new file with mode: 0644]
src/librustc_mir/borrow_check/nll/mod.rs [new file with mode: 0644]
src/librustc_mir/borrow_check/nll/region_infer.rs [new file with mode: 0644]
src/librustc_mir/borrow_check/nll/renumber.rs [new file with mode: 0644]
src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs [new file with mode: 0644]
src/librustc_mir/dataflow/impls/borrows.rs
src/librustc_mir/dataflow/move_paths/mod.rs
src/librustc_mir/transform/mod.rs
src/librustc_mir/transform/nll/constraint_generation.rs [deleted file]
src/librustc_mir/transform/nll/free_regions.rs [deleted file]
src/librustc_mir/transform/nll/mod.rs [deleted file]
src/librustc_mir/transform/nll/region_infer.rs [deleted file]
src/librustc_mir/transform/nll/renumber.rs [deleted file]
src/librustc_mir/transform/nll/subtype_constraint_generation.rs [deleted file]
src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop-uninitialized.rs [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop-with-fragment.rs [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop.rs [new file with mode: 0644]
src/test/ui/nll/maybe-initialized-drop.stderr [new file with mode: 0644]

diff --git a/src/librustc_mir/borrow_check.rs b/src/librustc_mir/borrow_check.rs
deleted file mode 100644 (file)
index ff38760..0000000
+++ /dev/null
@@ -1,2553 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! This query borrow-checks the MIR to (further) ensure it is not broken.
-
-use rustc::hir;
-use rustc::hir::def_id::DefId;
-use rustc::infer::InferCtxt;
-use rustc::ty::{self, ParamEnv, TyCtxt};
-use rustc::ty::maps::Providers;
-use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
-use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
-use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
-use transform::nll;
-
-use rustc_data_structures::fx::FxHashSet;
-use rustc_data_structures::indexed_set::{self, IdxSetBuf};
-use rustc_data_structures::indexed_vec::Idx;
-
-use syntax::ast;
-use syntax_pos::Span;
-
-use dataflow::do_dataflow;
-use dataflow::MoveDataParamEnv;
-use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer};
-use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
-use dataflow::{EverInitializedLvals, MovingOutStatements};
-use dataflow::{BorrowData, BorrowIndex, Borrows};
-use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
-use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex};
-use util::borrowck_errors::{BorrowckErrors, Origin};
-
-use self::MutateMode::{JustWrite, WriteAndRead};
-
-
-pub fn provide(providers: &mut Providers) {
-    *providers = Providers {
-        mir_borrowck,
-        ..*providers
-    };
-}
-
-fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
-    let input_mir = tcx.mir_validated(def_id);
-    debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
-
-    if {
-        !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir()
-            && !tcx.sess.opts.debugging_opts.nll
-    } {
-        return;
-    }
-
-    tcx.infer_ctxt().enter(|infcx| {
-        let input_mir: &Mir = &input_mir.borrow();
-        do_mir_borrowck(&infcx, input_mir, def_id);
-    });
-    debug!("mir_borrowck done");
-}
-
-fn do_mir_borrowck<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    input_mir: &Mir<'gcx>,
-    def_id: DefId,
-) {
-    let tcx = infcx.tcx;
-    let attributes = tcx.get_attrs(def_id);
-    let param_env = tcx.param_env(def_id);
-    let id = tcx.hir
-        .as_local_node_id(def_id)
-        .expect("do_mir_borrowck: non-local DefId");
-
-    let move_data: MoveData<'tcx> = match MoveData::gather_moves(input_mir, tcx) {
-        Ok(move_data) => move_data,
-        Err((move_data, move_errors)) => {
-            for move_error in move_errors {
-                let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
-                    MoveError::UnionMove { .. } => {
-                        unimplemented!("dont know how to report union move errors yet.")
-                    }
-                    MoveError::IllegalMove {
-                        cannot_move_out_of: o,
-                    } => (o.span, o.kind),
-                };
-                let origin = Origin::Mir;
-                let mut err = match kind {
-                    IllegalMoveOriginKind::Static => {
-                        tcx.cannot_move_out_of(span, "static item", origin)
-                    }
-                    IllegalMoveOriginKind::BorrowedContent => {
-                        tcx.cannot_move_out_of(span, "borrowed content", origin)
-                    }
-                    IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
-                        tcx.cannot_move_out_of_interior_of_drop(span, ty, origin)
-                    }
-                    IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
-                        tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin)
-                    }
-                };
-                err.emit();
-            }
-            move_data
-        }
-    };
-
-    // Make our own copy of the MIR. This copy will be modified (in place) to
-    // contain non-lexical lifetimes. It will have a lifetime tied
-    // to the inference context.
-    let mut mir: Mir<'tcx> = input_mir.clone();
-    let mir = &mut mir;
-
-    // If we are in non-lexical mode, compute the non-lexical lifetimes.
-    let opt_regioncx = if !tcx.sess.opts.debugging_opts.nll {
-        None
-    } else {
-        Some(nll::compute_regions(infcx, def_id, param_env, mir))
-    };
-
-    let mdpe = MoveDataParamEnv {
-        move_data: move_data,
-        param_env: param_env,
-    };
-    let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
-    let flow_borrows = do_dataflow(
-        tcx,
-        mir,
-        id,
-        &attributes,
-        &dead_unwinds,
-        Borrows::new(tcx, mir, opt_regioncx.as_ref()),
-        |bd, i| bd.location(i),
-    );
-    let flow_inits = do_dataflow(
-        tcx,
-        mir,
-        id,
-        &attributes,
-        &dead_unwinds,
-        MaybeInitializedLvals::new(tcx, mir, &mdpe),
-        |bd, i| &bd.move_data().move_paths[i],
-    );
-    let flow_uninits = do_dataflow(
-        tcx,
-        mir,
-        id,
-        &attributes,
-        &dead_unwinds,
-        MaybeUninitializedLvals::new(tcx, mir, &mdpe),
-        |bd, i| &bd.move_data().move_paths[i],
-    );
-    let flow_move_outs = do_dataflow(
-        tcx,
-        mir,
-        id,
-        &attributes,
-        &dead_unwinds,
-        MovingOutStatements::new(tcx, mir, &mdpe),
-        |bd, i| &bd.move_data().moves[i],
-    );
-    let flow_ever_inits = do_dataflow(
-        tcx,
-        mir,
-        id,
-        &attributes,
-        &dead_unwinds,
-        EverInitializedLvals::new(tcx, mir, &mdpe),
-        |bd, i| &bd.move_data().inits[i],
-    );
-
-    let mut mbcx = MirBorrowckCtxt {
-        tcx: tcx,
-        mir: mir,
-        node_id: id,
-        move_data: &mdpe.move_data,
-        param_env: param_env,
-        storage_dead_or_drop_error_reported: FxHashSet(),
-    };
-
-    let mut state = InProgress::new(
-        flow_borrows,
-        flow_inits,
-        flow_uninits,
-        flow_move_outs,
-        flow_ever_inits,
-    );
-
-    mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
-}
-
-#[allow(dead_code)]
-pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
-    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
-    mir: &'cx Mir<'tcx>,
-    node_id: ast::NodeId,
-    move_data: &'cx MoveData<'tcx>,
-    param_env: ParamEnv<'gcx>,
-    /// This field keeps track of when storage dead or drop errors are reported
-    /// in order to stop duplicate error reporting and identify the conditions required
-    /// for a "temporary value dropped here while still borrowed" error. See #45360.
-    storage_dead_or_drop_error_reported: FxHashSet<Local>,
-}
-
-// (forced to be `pub` due to its use as an associated type below.)
-pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> {
-    borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
-    inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
-    uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
-    move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
-    ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
-}
-
-struct FlowInProgress<BD>
-where
-    BD: BitDenotation,
-{
-    base_results: DataflowResults<BD>,
-    curr_state: IdxSetBuf<BD::Idx>,
-    stmt_gen: IdxSetBuf<BD::Idx>,
-    stmt_kill: IdxSetBuf<BD::Idx>,
-}
-
-// Check that:
-// 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
-// 2. loans made in overlapping scopes do not conflict
-// 3. assignments do not affect things loaned out as immutable
-// 4. moves do not affect things loaned out in any way
-impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-    type FlowState = InProgress<'cx, 'gcx, 'tcx>;
-
-    fn mir(&self) -> &'cx Mir<'tcx> {
-        self.mir
-    }
-
-    fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
-        flow_state.each_flow(
-            |b| b.reset_to_entry_of(bb),
-            |i| i.reset_to_entry_of(bb),
-            |u| u.reset_to_entry_of(bb),
-            |m| m.reset_to_entry_of(bb),
-            |e| e.reset_to_entry_of(bb),
-        );
-    }
-
-    fn reconstruct_statement_effect(
-        &mut self,
-        location: Location,
-        flow_state: &mut Self::FlowState,
-    ) {
-        flow_state.each_flow(
-            |b| b.reconstruct_statement_effect(location),
-            |i| i.reconstruct_statement_effect(location),
-            |u| u.reconstruct_statement_effect(location),
-            |m| m.reconstruct_statement_effect(location),
-            |e| e.reconstruct_statement_effect(location),
-        );
-    }
-
-    fn apply_local_effect(&mut self, _location: Location, flow_state: &mut Self::FlowState) {
-        flow_state.each_flow(
-            |b| b.apply_local_effect(),
-            |i| i.apply_local_effect(),
-            |u| u.apply_local_effect(),
-            |m| m.apply_local_effect(),
-            |e| e.apply_local_effect(),
-        );
-    }
-
-    fn reconstruct_terminator_effect(
-        &mut self,
-        location: Location,
-        flow_state: &mut Self::FlowState,
-    ) {
-        flow_state.each_flow(
-            |b| b.reconstruct_terminator_effect(location),
-            |i| i.reconstruct_terminator_effect(location),
-            |u| u.reconstruct_terminator_effect(location),
-            |m| m.reconstruct_terminator_effect(location),
-            |e| e.reconstruct_terminator_effect(location),
-        );
-    }
-
-    fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) {
-        let summary = flow_state.summary();
-        debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary);
-    }
-
-    fn visit_statement_entry(
-        &mut self,
-        location: Location,
-        stmt: &Statement<'tcx>,
-        flow_state: &Self::FlowState,
-    ) {
-        let summary = flow_state.summary();
-        debug!(
-            "MirBorrowckCtxt::process_statement({:?}, {:?}): {}",
-            location,
-            stmt,
-            summary
-        );
-        let span = stmt.source_info.span;
-        match stmt.kind {
-            StatementKind::Assign(ref lhs, ref rhs) => {
-                // NOTE: NLL RFC calls for *shallow* write; using Deep
-                // for short-term compat w/ AST-borrowck. Also, switch
-                // to shallow requires to dataflow: "if this is an
-                // assignment `place = <rvalue>`, then any loan for some
-                // path P of which `place` is a prefix is killed."
-                self.mutate_place(
-                    ContextKind::AssignLhs.new(location),
-                    (lhs, span),
-                    Deep,
-                    JustWrite,
-                    flow_state,
-                );
-
-                self.consume_rvalue(
-                    ContextKind::AssignRhs.new(location),
-                    (rhs, span),
-                    location,
-                    flow_state,
-                );
-            }
-            StatementKind::SetDiscriminant {
-                ref place,
-                variant_index: _,
-            } => {
-                self.mutate_place(
-                    ContextKind::SetDiscrim.new(location),
-                    (place, span),
-                    Shallow(Some(ArtificialField::Discriminant)),
-                    JustWrite,
-                    flow_state,
-                );
-            }
-            StatementKind::InlineAsm {
-                ref asm,
-                ref outputs,
-                ref inputs,
-            } => {
-                let context = ContextKind::InlineAsm.new(location);
-                for (o, output) in asm.outputs.iter().zip(outputs) {
-                    if o.is_indirect {
-                        // FIXME(eddyb) indirect inline asm outputs should
-                        // be encoeded through MIR place derefs instead.
-                        self.access_place(
-                            context,
-                            (output, span),
-                            (Deep, Read(ReadKind::Copy)),
-                            LocalMutationIsAllowed::No,
-                            flow_state,
-                        );
-                        self.check_if_path_is_moved(
-                            context,
-                            InitializationRequiringAction::Use,
-                            (output, span),
-                            flow_state,
-                        );
-                    } else {
-                        self.mutate_place(
-                            context,
-                            (output, span),
-                            Deep,
-                            if o.is_rw { WriteAndRead } else { JustWrite },
-                            flow_state,
-                        );
-                    }
-                }
-                for input in inputs {
-                    self.consume_operand(context, (input, span), flow_state);
-                }
-            }
-            StatementKind::EndRegion(ref _rgn) => {
-                // ignored when consuming results (update to
-                // flow_state already handled).
-            }
-            StatementKind::Nop | StatementKind::Validate(..) | StatementKind::StorageLive(..) => {
-                // `Nop`, `Validate`, and `StorageLive` are irrelevant
-                // to borrow check.
-            }
-
-            StatementKind::StorageDead(local) => {
-                self.access_place(
-                    ContextKind::StorageDead.new(location),
-                    (&Place::Local(local), span),
-                    (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
-                    LocalMutationIsAllowed::Yes,
-                    flow_state,
-                );
-            }
-        }
-    }
-
-    fn visit_terminator_entry(
-        &mut self,
-        location: Location,
-        term: &Terminator<'tcx>,
-        flow_state: &Self::FlowState,
-    ) {
-        let loc = location;
-        let summary = flow_state.summary();
-        debug!(
-            "MirBorrowckCtxt::process_terminator({:?}, {:?}): {}",
-            location,
-            term,
-            summary
-        );
-        let span = term.source_info.span;
-        match term.kind {
-            TerminatorKind::SwitchInt {
-                ref discr,
-                switch_ty: _,
-                values: _,
-                targets: _,
-            } => {
-                self.consume_operand(ContextKind::SwitchInt.new(loc), (discr, span), flow_state);
-            }
-            TerminatorKind::Drop {
-                location: ref drop_place,
-                target: _,
-                unwind: _,
-            } => {
-                self.access_place(
-                    ContextKind::Drop.new(loc),
-                    (drop_place, span),
-                    (Deep, Write(WriteKind::StorageDeadOrDrop)),
-                    LocalMutationIsAllowed::Yes,
-                    flow_state,
-                );
-            }
-            TerminatorKind::DropAndReplace {
-                location: ref drop_place,
-                value: ref new_value,
-                target: _,
-                unwind: _,
-            } => {
-                self.mutate_place(
-                    ContextKind::DropAndReplace.new(loc),
-                    (drop_place, span),
-                    Deep,
-                    JustWrite,
-                    flow_state,
-                );
-                self.consume_operand(
-                    ContextKind::DropAndReplace.new(loc),
-                    (new_value, span),
-                    flow_state,
-                );
-            }
-            TerminatorKind::Call {
-                ref func,
-                ref args,
-                ref destination,
-                cleanup: _,
-            } => {
-                self.consume_operand(ContextKind::CallOperator.new(loc), (func, span), flow_state);
-                for arg in args {
-                    self.consume_operand(
-                        ContextKind::CallOperand.new(loc),
-                        (arg, span),
-                        flow_state,
-                    );
-                }
-                if let Some((ref dest, _ /*bb*/)) = *destination {
-                    self.mutate_place(
-                        ContextKind::CallDest.new(loc),
-                        (dest, span),
-                        Deep,
-                        JustWrite,
-                        flow_state,
-                    );
-                }
-            }
-            TerminatorKind::Assert {
-                ref cond,
-                expected: _,
-                ref msg,
-                target: _,
-                cleanup: _,
-            } => {
-                self.consume_operand(ContextKind::Assert.new(loc), (cond, span), flow_state);
-                match *msg {
-                    AssertMessage::BoundsCheck { ref len, ref index } => {
-                        self.consume_operand(ContextKind::Assert.new(loc), (len, span), flow_state);
-                        self.consume_operand(
-                            ContextKind::Assert.new(loc),
-                            (index, span),
-                            flow_state,
-                        );
-                    }
-                    AssertMessage::Math(_ /*const_math_err*/) => {}
-                    AssertMessage::GeneratorResumedAfterReturn => {}
-                    AssertMessage::GeneratorResumedAfterPanic => {}
-                }
-            }
-
-            TerminatorKind::Yield {
-                ref value,
-                resume: _,
-                drop: _,
-            } => {
-                self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state);
-            }
-
-            TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
-                // Returning from the function implicitly kills storage for all locals and statics.
-                // Often, the storage will already have been killed by an explicit
-                // StorageDead, but we don't always emit those (notably on unwind paths),
-                // so this "extra check" serves as a kind of backup.
-                let domain = flow_state.borrows.base_results.operator();
-                let data = domain.borrows();
-                flow_state.borrows.with_elems_outgoing(|borrows| {
-                    for i in borrows {
-                        let borrow = &data[i];
-
-                        if self.place_is_invalidated_at_exit(&borrow.place) {
-                            debug!("borrow conflicts at exit {:?}", borrow);
-                            let borrow_span = self.mir.source_info(borrow.location).span;
-                            // FIXME: should be talking about the region lifetime instead
-                            // of just a span here.
-                            let end_span = domain.opt_region_end_span(&borrow.region);
-
-                            self.report_borrowed_value_does_not_live_long_enough(
-                                ContextKind::StorageDead.new(loc),
-                                (&borrow.place, borrow_span),
-                                end_span,
-                            )
-                        }
-                    }
-                });
-            }
-            TerminatorKind::Goto { target: _ } |
-            TerminatorKind::Unreachable |
-            TerminatorKind::FalseEdges { .. } => {
-                // no data used, thus irrelevant to borrowck
-            }
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum MutateMode {
-    JustWrite,
-    WriteAndRead,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum Control {
-    Continue,
-    Break,
-}
-
-use self::ShallowOrDeep::{Deep, Shallow};
-use self::ReadOrWrite::{Read, Write};
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum ArtificialField {
-    Discriminant,
-    ArrayLength,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum ShallowOrDeep {
-    /// From the RFC: "A *shallow* access means that the immediate
-    /// fields reached at LV are accessed, but references or pointers
-    /// found within are not dereferenced. Right now, the only access
-    /// that is shallow is an assignment like `x = ...;`, which would
-    /// be a *shallow write* of `x`."
-    Shallow(Option<ArtificialField>),
-
-    /// From the RFC: "A *deep* access means that all data reachable
-    /// through the given place may be invalidated or accesses by
-    /// this action."
-    Deep,
-}
-
-/// Kind of access to a value: read or write
-/// (For informational purposes only)
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum ReadOrWrite {
-    /// From the RFC: "A *read* means that the existing data may be
-    /// read, but will not be changed."
-    Read(ReadKind),
-
-    /// From the RFC: "A *write* means that the data may be mutated to
-    /// new values or otherwise invalidated (for example, it could be
-    /// de-initialized, as in a move operation).
-    Write(WriteKind),
-}
-
-/// Kind of read access to a value
-/// (For informational purposes only)
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum ReadKind {
-    Borrow(BorrowKind),
-    Copy,
-}
-
-/// Kind of write access to a value
-/// (For informational purposes only)
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum WriteKind {
-    StorageDeadOrDrop,
-    MutableBorrow(BorrowKind),
-    Mutate,
-    Move,
-}
-
-/// When checking permissions for a place access, this flag is used to indicate that an immutable
-/// local place can be mutated.
-///
-/// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications:
-/// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`
-/// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and
-///   `is_declared_mutable()`
-/// - Take flow state into consideration in `is_assignable()` for local variables
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum LocalMutationIsAllowed {
-    Yes,
-    No,
-}
-
-#[derive(Copy, Clone)]
-enum InitializationRequiringAction {
-    Update,
-    Borrow,
-    Use,
-    Assignment,
-}
-
-impl InitializationRequiringAction {
-    fn as_noun(self) -> &'static str {
-        match self {
-            InitializationRequiringAction::Update => "update",
-            InitializationRequiringAction::Borrow => "borrow",
-            InitializationRequiringAction::Use => "use",
-            InitializationRequiringAction::Assignment => "assign",
-        }
-    }
-
-    fn as_verb_in_past_tense(self) -> &'static str {
-        match self {
-            InitializationRequiringAction::Update => "updated",
-            InitializationRequiringAction::Borrow => "borrowed",
-            InitializationRequiringAction::Use => "used",
-            InitializationRequiringAction::Assignment => "assigned",
-        }
-    }
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-    /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
-    /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
-    /// place is initialized and (b) it is not borrowed in some way that would prevent this
-    /// access.
-    ///
-    /// Returns true if an error is reported, false otherwise.
-    fn access_place(
-        &mut self,
-        context: Context,
-        place_span: (&Place<'tcx>, Span),
-        kind: (ShallowOrDeep, ReadOrWrite),
-        is_local_mutation_allowed: LocalMutationIsAllowed,
-        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
-    ) {
-        let (sd, rw) = kind;
-
-        let storage_dead_or_drop_local = match (place_span.0, rw) {
-            (&Place::Local(local), Write(WriteKind::StorageDeadOrDrop)) => Some(local),
-            _ => None,
-        };
-
-        // Check if error has already been reported to stop duplicate reporting.
-        if let Some(local) = storage_dead_or_drop_local {
-            if self.storage_dead_or_drop_error_reported.contains(&local) {
-                return;
-            }
-        }
-
-        // Check permissions
-        let mut error_reported =
-            self.check_access_permissions(place_span, rw, is_local_mutation_allowed);
-
-        self.each_borrow_involving_path(
-            context,
-            (sd, place_span.0),
-            flow_state,
-            |this, _index, borrow, common_prefix| match (rw, borrow.kind) {
-                (Read(_), BorrowKind::Shared) => Control::Continue,
-                (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => {
-                    match kind {
-                        ReadKind::Copy => {
-                            error_reported = true;
-                            this.report_use_while_mutably_borrowed(context, place_span, borrow)
-                        }
-                        ReadKind::Borrow(bk) => {
-                            let end_issued_loan_span = flow_state
-                                .borrows
-                                .base_results
-                                .operator()
-                                .opt_region_end_span(&borrow.region);
-                            error_reported = true;
-                            this.report_conflicting_borrow(
-                                context,
-                                common_prefix,
-                                place_span,
-                                bk,
-                                &borrow,
-                                end_issued_loan_span,
-                            )
-                        }
-                    }
-                    Control::Break
-                }
-                (Write(kind), _) => {
-                    match kind {
-                        WriteKind::MutableBorrow(bk) => {
-                            let end_issued_loan_span = flow_state
-                                .borrows
-                                .base_results
-                                .operator()
-                                .opt_region_end_span(&borrow.region);
-                            error_reported = true;
-                            this.report_conflicting_borrow(
-                                context,
-                                common_prefix,
-                                place_span,
-                                bk,
-                                &borrow,
-                                end_issued_loan_span,
-                            )
-                        }
-                        WriteKind::StorageDeadOrDrop => {
-                            let end_span = flow_state
-                                .borrows
-                                .base_results
-                                .operator()
-                                .opt_region_end_span(&borrow.region);
-                            error_reported = true;
-                            this.report_borrowed_value_does_not_live_long_enough(
-                                context,
-                                place_span,
-                                end_span,
-                            )
-                        }
-                        WriteKind::Mutate => {
-                            error_reported = true;
-                            this.report_illegal_mutation_of_borrowed(context, place_span, borrow)
-                        }
-                        WriteKind::Move => {
-                            error_reported = true;
-                            this.report_move_out_while_borrowed(context, place_span, &borrow)
-                        }
-                    }
-                    Control::Break
-                }
-            },
-        );
-
-        if error_reported {
-            if let Some(local) = storage_dead_or_drop_local {
-                self.storage_dead_or_drop_error_reported.insert(local);
-            }
-        }
-    }
-
-    fn mutate_place(
-        &mut self,
-        context: Context,
-        place_span: (&Place<'tcx>, Span),
-        kind: ShallowOrDeep,
-        mode: MutateMode,
-        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
-    ) {
-        // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
-        match mode {
-            MutateMode::WriteAndRead => {
-                self.check_if_path_is_moved(
-                    context,
-                    InitializationRequiringAction::Update,
-                    place_span,
-                    flow_state,
-                );
-            }
-            MutateMode::JustWrite => {
-                self.check_if_assigned_path_is_moved(context, place_span, flow_state);
-            }
-        }
-
-        self.access_place(
-            context,
-            place_span,
-            (kind, Write(WriteKind::Mutate)),
-            LocalMutationIsAllowed::Yes,
-            flow_state,
-        );
-
-        // check for reassignments to immutable local variables
-        self.check_if_reassignment_to_immutable_state(context, place_span, flow_state);
-    }
-
-    fn consume_rvalue(
-        &mut self,
-        context: Context,
-        (rvalue, span): (&Rvalue<'tcx>, Span),
-        _location: Location,
-        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
-    ) {
-        match *rvalue {
-            Rvalue::Ref(_ /*rgn*/, bk, ref place) => {
-                let access_kind = match bk {
-                    BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
-                    BorrowKind::Unique | BorrowKind::Mut => {
-                        (Deep, Write(WriteKind::MutableBorrow(bk)))
-                    }
-                };
-                self.access_place(
-                    context,
-                    (place, span),
-                    access_kind,
-                    LocalMutationIsAllowed::No,
-                    flow_state,
-                );
-                self.check_if_path_is_moved(
-                    context,
-                    InitializationRequiringAction::Borrow,
-                    (place, span),
-                    flow_state,
-                );
-            }
-
-            Rvalue::Use(ref operand) |
-            Rvalue::Repeat(ref operand, _) |
-            Rvalue::UnaryOp(_ /*un_op*/, ref operand) |
-            Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => {
-                self.consume_operand(context, (operand, span), flow_state)
-            }
-
-            Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => {
-                let af = match *rvalue {
-                    Rvalue::Len(..) => ArtificialField::ArrayLength,
-                    Rvalue::Discriminant(..) => ArtificialField::Discriminant,
-                    _ => unreachable!(),
-                };
-                self.access_place(
-                    context,
-                    (place, span),
-                    (Shallow(Some(af)), Read(ReadKind::Copy)),
-                    LocalMutationIsAllowed::No,
-                    flow_state,
-                );
-                self.check_if_path_is_moved(
-                    context,
-                    InitializationRequiringAction::Use,
-                    (place, span),
-                    flow_state,
-                );
-            }
-
-            Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) |
-            Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => {
-                self.consume_operand(context, (operand1, span), flow_state);
-                self.consume_operand(context, (operand2, span), flow_state);
-            }
-
-            Rvalue::NullaryOp(_op, _ty) => {
-                // nullary ops take no dynamic input; no borrowck effect.
-                //
-                // FIXME: is above actually true? Do we want to track
-                // the fact that uninitialized data can be created via
-                // `NullOp::Box`?
-            }
-
-            Rvalue::Aggregate(ref _aggregate_kind, ref operands) => for operand in operands {
-                self.consume_operand(context, (operand, span), flow_state);
-            },
-        }
-    }
-
-    fn consume_operand(
-        &mut self,
-        context: Context,
-        (operand, span): (&Operand<'tcx>, Span),
-        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
-    ) {
-        match *operand {
-            Operand::Copy(ref place) => {
-                // copy of place: check if this is "copy of frozen path"
-                // (FIXME: see check_loans.rs)
-                self.access_place(
-                    context,
-                    (place, span),
-                    (Deep, Read(ReadKind::Copy)),
-                    LocalMutationIsAllowed::No,
-                    flow_state,
-                );
-
-                // Finally, check if path was already moved.
-                self.check_if_path_is_moved(
-                    context,
-                    InitializationRequiringAction::Use,
-                    (place, span),
-                    flow_state,
-                );
-            }
-            Operand::Move(ref place) => {
-                // move of place: check if this is move of already borrowed path
-                self.access_place(
-                    context,
-                    (place, span),
-                    (Deep, Write(WriteKind::Move)),
-                    LocalMutationIsAllowed::Yes,
-                    flow_state,
-                );
-
-                // Finally, check if path was already moved.
-                self.check_if_path_is_moved(
-                    context,
-                    InitializationRequiringAction::Use,
-                    (place, span),
-                    flow_state,
-                );
-            }
-            Operand::Constant(_) => {}
-        }
-    }
-
-    /// Returns whether a borrow of this place is invalidated when the function
-    /// exits
-    fn place_is_invalidated_at_exit(&self, place: &Place<'tcx>) -> bool {
-        debug!("place_is_invalidated_at_exit({:?})", place);
-        let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
-
-        // FIXME(nll-rfc#40): do more precise destructor tracking here. For now
-        // we just know that all locals are dropped at function exit (otherwise
-        // we'll have a memory leak) and assume that all statics have a destructor.
-        let (might_be_alive, will_be_dropped) = match root_place {
-            Place::Static(statik) => {
-                // Thread-locals might be dropped after the function exits, but
-                // "true" statics will never be.
-                let is_thread_local = self.tcx
-                    .get_attrs(statik.def_id)
-                    .iter()
-                    .any(|attr| attr.check_name("thread_local"));
-
-                (true, is_thread_local)
-            }
-            Place::Local(_) => {
-                // Locals are always dropped at function exit, and if they
-                // have a destructor it would've been called already.
-                (false, true)
-            }
-            Place::Projection(..) => {
-                bug!("root of {:?} is a projection ({:?})?", place, root_place)
-            }
-        };
-
-        if !will_be_dropped {
-            debug!(
-                "place_is_invalidated_at_exit({:?}) - won't be dropped",
-                place
-            );
-            return false;
-        }
-
-        // FIXME: replace this with a proper borrow_conflicts_with_place when
-        // that is merged.
-        let prefix_set = if might_be_alive {
-            PrefixSet::Supporting
-        } else {
-            PrefixSet::Shallow
-        };
-
-        self.prefixes(place, prefix_set)
-            .any(|prefix| prefix == root_place)
-    }
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-    fn check_if_reassignment_to_immutable_state(
-        &mut self,
-        context: Context,
-        (place, span): (&Place<'tcx>, Span),
-        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
-    ) {
-        let move_data = self.move_data;
-
-        // determine if this path has a non-mut owner (and thus needs checking).
-        if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) {
-            return;
-        }
-
-        if let Err(_) = self.is_mutable(place, LocalMutationIsAllowed::Yes) {
-            return;
-        }
-
-        match self.move_path_closest_to(place) {
-            Ok(mpi) => for ii in &move_data.init_path_map[mpi] {
-                if flow_state.ever_inits.curr_state.contains(ii) {
-                    let first_assign_span = self.move_data.inits[*ii].span;
-                    self.report_illegal_reassignment(context, (place, span), first_assign_span);
-                    break;
-                }
-            },
-            Err(NoMovePathFound::ReachedStatic) => {
-                let item_msg = match self.describe_place(place) {
-                    Some(name) => format!("immutable static item `{}`", name),
-                    None => "immutable static item".to_owned(),
-                };
-                self.tcx.sess.delay_span_bug(
-                    span,
-                    &format!(
-                        "cannot assign to {}, should have been caught by \
-                         `check_access_permissions()`",
-                        item_msg
-                    ),
-                );
-            }
-        }
-    }
-
-    fn check_if_path_is_moved(
-        &mut self,
-        context: Context,
-        desired_action: InitializationRequiringAction,
-        place_span: (&Place<'tcx>, Span),
-        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
-    ) {
-        // FIXME: analogous code in check_loans first maps `place` to
-        // its base_path ... but is that what we want here?
-        let place = self.base_path(place_span.0);
-
-        let maybe_uninits = &flow_state.uninits;
-        let curr_move_outs = &flow_state.move_outs.curr_state;
-
-        // Bad scenarios:
-        //
-        // 1. Move of `a.b.c`, use of `a.b.c`
-        // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
-        // 3. Move of `a.b.c`, use of `a` or `a.b`
-        // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
-        //    partial initialization support, one might have `a.x`
-        //    initialized but not `a.b`.
-        //
-        // OK scenarios:
-        //
-        // 5. Move of `a.b.c`, use of `a.b.d`
-        // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
-        // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
-        //    must have been initialized for the use to be sound.
-        // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
-
-        // The dataflow tracks shallow prefixes distinctly (that is,
-        // field-accesses on P distinctly from P itself), in order to
-        // track substructure initialization separately from the whole
-        // structure.
-        //
-        // E.g., when looking at (*a.b.c).d, if the closest prefix for
-        // which we have a MovePath is `a.b`, then that means that the
-        // initialization state of `a.b` is all we need to inspect to
-        // know if `a.b.c` is valid (and from that we infer that the
-        // dereference and `.d` access is also valid, since we assume
-        // `a.b.c` is assigned a reference to a initialized and
-        // well-formed record structure.)
-
-        // Therefore, if we seek out the *closest* prefix for which we
-        // have a MovePath, that should capture the initialization
-        // state for the place scenario.
-        //
-        // This code covers scenarios 1, 2, and 4.
-
-        debug!("check_if_path_is_moved part1 place: {:?}", place);
-        match self.move_path_closest_to(place) {
-            Ok(mpi) => {
-                if maybe_uninits.curr_state.contains(&mpi) {
-                    self.report_use_of_moved_or_uninitialized(
-                        context,
-                        desired_action,
-                        place_span,
-                        mpi,
-                        curr_move_outs,
-                    );
-                    return; // don't bother finding other problems.
-                }
-            }
-            Err(NoMovePathFound::ReachedStatic) => {
-                // Okay: we do not build MoveData for static variables
-            } // Only query longest prefix with a MovePath, not further
-              // ancestors; dataflow recurs on children when parents
-              // move (to support partial (re)inits).
-              //
-              // (I.e. querying parents breaks scenario 8; but may want
-              // to do such a query based on partial-init feature-gate.)
-        }
-
-        // A move of any shallow suffix of `place` also interferes
-        // with an attempt to use `place`. This is scenario 3 above.
-        //
-        // (Distinct from handling of scenarios 1+2+4 above because
-        // `place` does not interfere with suffixes of its prefixes,
-        // e.g. `a.b.c` does not interfere with `a.b.d`)
-
-        debug!("check_if_path_is_moved part2 place: {:?}", place);
-        if let Some(mpi) = self.move_path_for_place(place) {
-            if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) {
-                self.report_use_of_moved_or_uninitialized(
-                    context,
-                    desired_action,
-                    place_span,
-                    child_mpi,
-                    curr_move_outs,
-                );
-                return; // don't bother finding other problems.
-            }
-        }
-    }
-
-    /// Currently MoveData does not store entries for all places in
-    /// the input MIR. For example it will currently filter out
-    /// places that are Copy; thus we do not track places of shared
-    /// reference type. This routine will walk up a place along its
-    /// prefixes, searching for a foundational place that *is*
-    /// tracked in the MoveData.
-    ///
-    /// An Err result includes a tag indicated why the search failed.
-    /// Currenly this can only occur if the place is built off of a
-    /// static variable, as we do not track those in the MoveData.
-    fn move_path_closest_to(
-        &mut self,
-        place: &Place<'tcx>,
-    ) -> Result<MovePathIndex, NoMovePathFound> {
-        let mut last_prefix = place;
-        for prefix in self.prefixes(place, PrefixSet::All) {
-            if let Some(mpi) = self.move_path_for_place(prefix) {
-                return Ok(mpi);
-            }
-            last_prefix = prefix;
-        }
-        match *last_prefix {
-            Place::Local(_) => panic!("should have move path for every Local"),
-            Place::Projection(_) => panic!("PrefixSet::All meant dont stop for Projection"),
-            Place::Static(_) => return Err(NoMovePathFound::ReachedStatic),
-        }
-    }
-
-    fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option<MovePathIndex> {
-        // If returns None, then there is no move path corresponding
-        // to a direct owner of `place` (which means there is nothing
-        // that borrowck tracks for its analysis).
-
-        match self.move_data.rev_lookup.find(place) {
-            LookupResult::Parent(_) => None,
-            LookupResult::Exact(mpi) => Some(mpi),
-        }
-    }
-
-    fn check_if_assigned_path_is_moved(
-        &mut self,
-        context: Context,
-        (place, span): (&Place<'tcx>, Span),
-        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
-    ) {
-        // recur down place; dispatch to check_if_path_is_moved when necessary
-        let mut place = place;
-        loop {
-            match *place {
-                Place::Local(_) | Place::Static(_) => {
-                    // assigning to `x` does not require `x` be initialized.
-                    break;
-                }
-                Place::Projection(ref proj) => {
-                    let Projection { ref base, ref elem } = **proj;
-                    match *elem {
-                        ProjectionElem::Deref |
-                        // assigning to *P requires `P` initialized.
-                        ProjectionElem::Index(_/*operand*/) |
-                        ProjectionElem::ConstantIndex { .. } |
-                        // assigning to P[i] requires `P` initialized.
-                        ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
-                        // assigning to (P->variant) is okay if assigning to `P` is okay
-                        //
-                        // FIXME: is this true even if P is a adt with a dtor?
-                        { }
-
-                        ProjectionElem::Subslice { .. } => {
-                            panic!("we dont allow assignments to subslices, context: {:?}",
-                                   context);
-                        }
-
-                        ProjectionElem::Field(..) => {
-                            // if type of `P` has a dtor, then
-                            // assigning to `P.f` requires `P` itself
-                            // be already initialized
-                            let tcx = self.tcx;
-                            match base.ty(self.mir, tcx).to_ty(tcx).sty {
-                                ty::TyAdt(def, _) if def.has_dtor(tcx) => {
-
-                                    // FIXME: analogous code in
-                                    // check_loans.rs first maps
-                                    // `base` to its base_path.
-
-                                    self.check_if_path_is_moved(
-                                        context, InitializationRequiringAction::Assignment,
-                                        (base, span), flow_state);
-
-                                    // (base initialized; no need to
-                                    // recur further)
-                                    break;
-                                }
-                                _ => {}
-                            }
-                        }
-                    }
-
-                    place = base;
-                    continue;
-                }
-            }
-        }
-    }
-
-    /// Check the permissions for the given place and read or write kind
-    ///
-    /// Returns true if an error is reported, false otherwise.
-    fn check_access_permissions(
-        &self,
-        (place, span): (&Place<'tcx>, Span),
-        kind: ReadOrWrite,
-        is_local_mutation_allowed: LocalMutationIsAllowed,
-    ) -> bool {
-        debug!(
-            "check_access_permissions({:?}, {:?}, {:?})",
-            place,
-            kind,
-            is_local_mutation_allowed
-        );
-        let mut error_reported = false;
-        match kind {
-            Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => {
-                if let Err(_place_err) = self.is_unique(place) {
-                    span_bug!(span, "&unique borrow for {:?} should not fail", place);
-                }
-            }
-            Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) =
-                self.is_mutable(place, is_local_mutation_allowed)
-            {
-                error_reported = true;
-
-                let item_msg = match self.describe_place(place) {
-                    Some(name) => format!("immutable item `{}`", name),
-                    None => "immutable item".to_owned(),
-                };
-
-                let mut err = self.tcx
-                    .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir);
-                err.span_label(span, "cannot borrow as mutable");
-
-                if place != place_err {
-                    if let Some(name) = self.describe_place(place_err) {
-                        err.note(&format!("Value not mutable causing this error: `{}`", name));
-                    }
-                }
-
-                err.emit();
-            },
-            Write(WriteKind::Mutate) => {
-                if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) {
-                    error_reported = true;
-
-                    let item_msg = match self.describe_place(place) {
-                        Some(name) => format!("immutable item `{}`", name),
-                        None => "immutable item".to_owned(),
-                    };
-
-                    let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir);
-                    err.span_label(span, "cannot mutate");
-
-                    if place != place_err {
-                        if let Some(name) = self.describe_place(place_err) {
-                            err.note(&format!("Value not mutable causing this error: `{}`", name));
-                        }
-                    }
-
-                    err.emit();
-                }
-            }
-            Write(WriteKind::Move) |
-            Write(WriteKind::StorageDeadOrDrop) |
-            Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => {
-                if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
-                    self.tcx.sess.delay_span_bug(
-                        span,
-                        &format!(
-                            "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
-                            place,
-                            kind
-                        ),
-                    );
-                }
-            }
-            Read(ReadKind::Borrow(BorrowKind::Unique)) |
-            Read(ReadKind::Borrow(BorrowKind::Mut)) |
-            Read(ReadKind::Borrow(BorrowKind::Shared)) |
-            Read(ReadKind::Copy) => {} // Access authorized
-        }
-
-        error_reported
-    }
-
-    /// Can this value be written or borrowed mutably
-    fn is_mutable<'d>(
-        &self,
-        place: &'d Place<'tcx>,
-        is_local_mutation_allowed: LocalMutationIsAllowed,
-    ) -> Result<(), &'d Place<'tcx>> {
-        match *place {
-            Place::Local(local) => {
-                let local = &self.mir.local_decls[local];
-                match local.mutability {
-                    Mutability::Not => match is_local_mutation_allowed {
-                        LocalMutationIsAllowed::Yes => Ok(()),
-                        LocalMutationIsAllowed::No => Err(place),
-                    },
-                    Mutability::Mut => Ok(()),
-                }
-            }
-            Place::Static(ref static_) => if !self.tcx.is_static_mut(static_.def_id) {
-                Err(place)
-            } else {
-                Ok(())
-            },
-            Place::Projection(ref proj) => {
-                match proj.elem {
-                    ProjectionElem::Deref => {
-                        let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
-
-                        // Check the kind of deref to decide
-                        match base_ty.sty {
-                            ty::TyRef(_, tnm) => {
-                                match tnm.mutbl {
-                                    // Shared borrowed data is never mutable
-                                    hir::MutImmutable => Err(place),
-                                    // Mutably borrowed data is mutable, but only if we have a
-                                    // unique path to the `&mut`
-                                    hir::MutMutable => {
-                                        if self.is_upvar_field_projection(&proj.base).is_some() {
-                                            self.is_mutable(&proj.base, is_local_mutation_allowed)
-                                        } else {
-                                            self.is_unique(&proj.base)
-                                        }
-                                    }
-                                }
-                            }
-                            ty::TyRawPtr(tnm) => {
-                                match tnm.mutbl {
-                                    // `*const` raw pointers are not mutable
-                                    hir::MutImmutable => Err(place),
-                                    // `*mut` raw pointers are always mutable, regardless of context
-                                    // The users have to check by themselve.
-                                    hir::MutMutable => Ok(()),
-                                }
-                            }
-                            // `Box<T>` owns its content, so mutable if its location is mutable
-                            _ if base_ty.is_box() => {
-                                self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
-                            }
-                            // Deref should only be for reference, pointers or boxes
-                            _ => bug!("Deref of unexpected type: {:?}", base_ty),
-                        }
-                    }
-                    // All other projections are owned by their base path, so mutable if
-                    // base path is mutable
-                    ProjectionElem::Field(..) |
-                    ProjectionElem::Index(..) |
-                    ProjectionElem::ConstantIndex { .. } |
-                    ProjectionElem::Subslice { .. } |
-                    ProjectionElem::Downcast(..) => {
-                        let field_projection = self.is_upvar_field_projection(place);
-
-                        if let Some(field) = field_projection {
-                            let decl = &self.mir.upvar_decls[field.index()];
-
-                            return match decl.mutability {
-                                Mutability::Mut => self.is_unique(&proj.base),
-                                Mutability::Not => Err(place),
-                            };
-                        }
-
-                        self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
-                    }
-                }
-            }
-        }
-    }
-
-    /// Does this place have a unique path
-    fn is_unique<'d>(&self, place: &'d Place<'tcx>) -> Result<(), &'d Place<'tcx>> {
-        match *place {
-            Place::Local(..) => {
-                // Local variables are unique
-                Ok(())
-            }
-            Place::Static(..) => {
-                // Static variables are not
-                Err(place)
-            }
-            Place::Projection(ref proj) => {
-                match proj.elem {
-                    ProjectionElem::Deref => {
-                        let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
-
-                        // `Box<T>` referent is unique if box is a unique spot
-                        if base_ty.is_box() {
-                            return self.is_unique(&proj.base);
-                        }
-
-                        // Otherwise we check the kind of deref to decide
-                        match base_ty.sty {
-                            ty::TyRef(_, tnm) => {
-                                match tnm.mutbl {
-                                    // place represent an aliased location
-                                    hir::MutImmutable => Err(place),
-                                    // `&mut T` is as unique as the context in which it is found
-                                    hir::MutMutable => self.is_unique(&proj.base),
-                                }
-                            }
-                            ty::TyRawPtr(tnm) => {
-                                match tnm.mutbl {
-                                    // `*mut` can be aliased, but we leave it to user
-                                    hir::MutMutable => Ok(()),
-                                    // `*const` is treated the same as `*mut`
-                                    hir::MutImmutable => Ok(()),
-                                }
-                            }
-                            // Deref should only be for reference, pointers or boxes
-                            _ => bug!("Deref of unexpected type: {:?}", base_ty),
-                        }
-                    }
-                    // Other projections are unique if the base is unique
-                    ProjectionElem::Field(..) |
-                    ProjectionElem::Index(..) |
-                    ProjectionElem::ConstantIndex { .. } |
-                    ProjectionElem::Subslice { .. } |
-                    ProjectionElem::Downcast(..) => self.is_unique(&proj.base),
-                }
-            }
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum NoMovePathFound {
-    ReachedStatic,
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-    fn each_borrow_involving_path<F>(
-        &mut self,
-        _context: Context,
-        access_place: (ShallowOrDeep, &Place<'tcx>),
-        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
-        mut op: F,
-    ) where
-        F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control,
-    {
-        let (access, place) = access_place;
-
-        // FIXME: analogous code in check_loans first maps `place` to
-        // its base_path.
-
-        let domain = flow_state.borrows.base_results.operator();
-        let data = domain.borrows();
-
-        // check for loan restricting path P being used. Accounts for
-        // borrows of P, P.a.b, etc.
-        'next_borrow: for i in flow_state.borrows.elems_incoming() {
-            let borrowed = &data[i];
-
-            // Is `place` (or a prefix of it) already borrowed? If
-            // so, that's relevant.
-            //
-            // FIXME: Differs from AST-borrowck; includes drive-by fix
-            // to #38899. Will probably need back-compat mode flag.
-            for accessed_prefix in self.prefixes(place, PrefixSet::All) {
-                if *accessed_prefix == borrowed.place {
-                    // FIXME: pass in enum describing case we are in?
-                    let ctrl = op(self, i, borrowed, accessed_prefix);
-                    if ctrl == Control::Break {
-                        return;
-                    }
-                }
-            }
-
-            // Is `place` a prefix (modulo access type) of the
-            // `borrowed.place`? If so, that's relevant.
-
-            let prefix_kind = match access {
-                Shallow(Some(ArtificialField::Discriminant)) |
-                Shallow(Some(ArtificialField::ArrayLength)) => {
-                    // The discriminant and array length are like
-                    // additional fields on the type; they do not
-                    // overlap any existing data there. Furthermore,
-                    // they cannot actually be a prefix of any
-                    // borrowed place (at least in MIR as it is
-                    // currently.)
-                    continue 'next_borrow;
-                }
-                Shallow(None) => PrefixSet::Shallow,
-                Deep => PrefixSet::Supporting,
-            };
-
-            for borrowed_prefix in self.prefixes(&borrowed.place, prefix_kind) {
-                if borrowed_prefix == place {
-                    // FIXME: pass in enum describing case we are in?
-                    let ctrl = op(self, i, borrowed, borrowed_prefix);
-                    if ctrl == Control::Break {
-                        return;
-                    }
-                }
-            }
-        }
-    }
-}
-
-use self::prefixes::PrefixSet;
-
-/// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an
-/// place are formed by stripping away fields and derefs, except that
-/// we stop when we reach the deref of a shared reference. [...] "
-///
-/// "Shallow prefixes are found by stripping away fields, but stop at
-/// any dereference. So: writing a path like `a` is illegal if `a.b`
-/// is borrowed. But: writing `a` is legal if `*a` is borrowed,
-/// whether or not `a` is a shared or mutable reference. [...] "
-mod prefixes {
-    use super::MirBorrowckCtxt;
-
-    use rustc::hir;
-    use rustc::ty::{self, TyCtxt};
-    use rustc::mir::{Mir, Place, ProjectionElem};
-
-    pub trait IsPrefixOf<'tcx> {
-        fn is_prefix_of(&self, other: &Place<'tcx>) -> bool;
-    }
-
-    impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> {
-        fn is_prefix_of(&self, other: &Place<'tcx>) -> bool {
-            let mut cursor = other;
-            loop {
-                if self == cursor {
-                    return true;
-                }
-
-                match *cursor {
-                    Place::Local(_) | Place::Static(_) => return false,
-                    Place::Projection(ref proj) => {
-                        cursor = &proj.base;
-                    }
-                }
-            }
-        }
-    }
-
-
-    pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
-        mir: &'cx Mir<'tcx>,
-        tcx: TyCtxt<'cx, 'gcx, 'tcx>,
-        kind: PrefixSet,
-        next: Option<&'cx Place<'tcx>>,
-    }
-
-    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
-    pub(super) enum PrefixSet {
-        /// Doesn't stop until it returns the base case (a Local or
-        /// Static prefix).
-        All,
-        /// Stops at any dereference.
-        Shallow,
-        /// Stops at the deref of a shared reference.
-        Supporting,
-    }
-
-    impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-        /// Returns an iterator over the prefixes of `place`
-        /// (inclusive) from longest to smallest, potentially
-        /// terminating the iteration early based on `kind`.
-        pub(super) fn prefixes(
-            &self,
-            place: &'cx Place<'tcx>,
-            kind: PrefixSet,
-        ) -> Prefixes<'cx, 'gcx, 'tcx> {
-            Prefixes {
-                next: Some(place),
-                kind,
-                mir: self.mir,
-                tcx: self.tcx,
-            }
-        }
-    }
-
-    impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> {
-        type Item = &'cx Place<'tcx>;
-        fn next(&mut self) -> Option<Self::Item> {
-            let mut cursor = match self.next {
-                None => return None,
-                Some(place) => place,
-            };
-
-            // Post-processing `place`: Enqueue any remaining
-            // work. Also, `place` may not be a prefix itself, but
-            // may hold one further down (e.g. we never return
-            // downcasts here, but may return a base of a downcast).
-
-            'cursor: loop {
-                let proj = match *cursor {
-                    Place::Local(_) | // search yielded this leaf
-                    Place::Static(_) => {
-                        self.next = None;
-                        return Some(cursor);
-                    }
-
-                    Place::Projection(ref proj) => proj,
-                };
-
-                match proj.elem {
-                    ProjectionElem::Field(_ /*field*/, _ /*ty*/) => {
-                        // FIXME: add union handling
-                        self.next = Some(&proj.base);
-                        return Some(cursor);
-                    }
-                    ProjectionElem::Downcast(..) |
-                    ProjectionElem::Subslice { .. } |
-                    ProjectionElem::ConstantIndex { .. } |
-                    ProjectionElem::Index(_) => {
-                        cursor = &proj.base;
-                        continue 'cursor;
-                    }
-                    ProjectionElem::Deref => {
-                        // (handled below)
-                    }
-                }
-
-                assert_eq!(proj.elem, ProjectionElem::Deref);
-
-                match self.kind {
-                    PrefixSet::Shallow => {
-                        // shallow prefixes are found by stripping away
-                        // fields, but stop at *any* dereference.
-                        // So we can just stop the traversal now.
-                        self.next = None;
-                        return Some(cursor);
-                    }
-                    PrefixSet::All => {
-                        // all prefixes: just blindly enqueue the base
-                        // of the projection
-                        self.next = Some(&proj.base);
-                        return Some(cursor);
-                    }
-                    PrefixSet::Supporting => {
-                        // fall through!
-                    }
-                }
-
-                assert_eq!(self.kind, PrefixSet::Supporting);
-                // supporting prefixes: strip away fields and
-                // derefs, except we stop at the deref of a shared
-                // reference.
-
-                let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
-                match ty.sty {
-                    ty::TyRawPtr(_) |
-                    ty::TyRef(
-                        _, /*rgn*/
-                        ty::TypeAndMut {
-                            ty: _,
-                            mutbl: hir::MutImmutable,
-                        },
-                    ) => {
-                        // don't continue traversing over derefs of raw pointers or shared borrows.
-                        self.next = None;
-                        return Some(cursor);
-                    }
-
-                    ty::TyRef(
-                        _, /*rgn*/
-                        ty::TypeAndMut {
-                            ty: _,
-                            mutbl: hir::MutMutable,
-                        },
-                    ) => {
-                        self.next = Some(&proj.base);
-                        return Some(cursor);
-                    }
-
-                    ty::TyAdt(..) if ty.is_box() => {
-                        self.next = Some(&proj.base);
-                        return Some(cursor);
-                    }
-
-                    _ => panic!("unknown type fed to Projection Deref."),
-                }
-            }
-        }
-    }
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-    fn report_use_of_moved_or_uninitialized(
-        &mut self,
-        _context: Context,
-        desired_action: InitializationRequiringAction,
-        (place, span): (&Place<'tcx>, Span),
-        mpi: MovePathIndex,
-        curr_move_out: &IdxSetBuf<MoveOutIndex>,
-    ) {
-        let mois = self.move_data.path_map[mpi]
-            .iter()
-            .filter(|moi| curr_move_out.contains(moi))
-            .collect::<Vec<_>>();
-
-        if mois.is_empty() {
-            let item_msg = match self.describe_place(place) {
-                Some(name) => format!("`{}`", name),
-                None => "value".to_owned(),
-            };
-            self.tcx
-                .cannot_act_on_uninitialized_variable(
-                    span,
-                    desired_action.as_noun(),
-                    &self.describe_place(place).unwrap_or("_".to_owned()),
-                    Origin::Mir,
-                )
-                .span_label(span, format!("use of possibly uninitialized {}", item_msg))
-                .emit();
-        } else {
-            let msg = ""; //FIXME: add "partially " or "collaterally "
-
-            let mut err = self.tcx.cannot_act_on_moved_value(
-                span,
-                desired_action.as_noun(),
-                msg,
-                &self.describe_place(place).unwrap_or("_".to_owned()),
-                Origin::Mir,
-            );
-
-            err.span_label(
-                span,
-                format!(
-                    "value {} here after move",
-                    desired_action.as_verb_in_past_tense()
-                ),
-            );
-            for moi in mois {
-                let move_msg = ""; //FIXME: add " (into closure)"
-                let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
-                if span == move_span {
-                    err.span_label(
-                        span,
-                        format!("value moved{} here in previous iteration of loop", move_msg),
-                    );
-                } else {
-                    err.span_label(move_span, format!("value moved{} here", move_msg));
-                };
-            }
-            //FIXME: add note for closure
-            err.emit();
-        }
-    }
-
-    fn report_move_out_while_borrowed(
-        &mut self,
-        _context: Context,
-        (place, span): (&Place<'tcx>, Span),
-        borrow: &BorrowData<'tcx>,
-    ) {
-        let value_msg = match self.describe_place(place) {
-            Some(name) => format!("`{}`", name),
-            None => "value".to_owned(),
-        };
-        let borrow_msg = match self.describe_place(&borrow.place) {
-            Some(name) => format!("`{}`", name),
-            None => "value".to_owned(),
-        };
-        self.tcx
-            .cannot_move_when_borrowed(
-                span,
-                &self.describe_place(place).unwrap_or("_".to_owned()),
-                Origin::Mir,
-            )
-            .span_label(
-                self.retrieve_borrow_span(borrow),
-                format!("borrow of {} occurs here", borrow_msg),
-            )
-            .span_label(span, format!("move out of {} occurs here", value_msg))
-            .emit();
-    }
-
-    fn report_use_while_mutably_borrowed(
-        &mut self,
-        _context: Context,
-        (place, span): (&Place<'tcx>, Span),
-        borrow: &BorrowData<'tcx>,
-    ) {
-        let mut err = self.tcx.cannot_use_when_mutably_borrowed(
-            span,
-            &self.describe_place(place).unwrap_or("_".to_owned()),
-            self.retrieve_borrow_span(borrow),
-            &self.describe_place(&borrow.place).unwrap_or("_".to_owned()),
-            Origin::Mir,
-        );
-
-        err.emit();
-    }
-
-    /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
-    /// the local assigned at `location`.
-    /// This is done by searching in statements succeeding `location`
-    /// and originating from `maybe_closure_span`.
-    fn find_closure_span(
-        &self,
-        maybe_closure_span: Span,
-        location: Location,
-    ) -> Option<(Span, Span)> {
-        use rustc::hir::ExprClosure;
-        use rustc::mir::AggregateKind;
-
-        let local = if let StatementKind::Assign(Place::Local(local), _) =
-            self.mir[location.block].statements[location.statement_index].kind
-        {
-            local
-        } else {
-            return None;
-        };
-
-        for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
-            if maybe_closure_span != stmt.source_info.span {
-                break;
-            }
-
-            if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
-                if let AggregateKind::Closure(def_id, _) = **kind {
-                    debug!("find_closure_span: found closure {:?}", places);
-
-                    return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
-                        let args_span = if let ExprClosure(_, _, _, span, _) =
-                            self.tcx.hir.expect_expr(node_id).node
-                        {
-                            span
-                        } else {
-                            return None;
-                        };
-
-                        self.tcx
-                            .with_freevars(node_id, |freevars| {
-                                for (v, place) in freevars.iter().zip(places) {
-                                    match *place {
-                                        Operand::Copy(Place::Local(l)) |
-                                        Operand::Move(Place::Local(l)) if local == l =>
-                                        {
-                                            debug!(
-                                                "find_closure_span: found captured local {:?}",
-                                                l
-                                            );
-                                            return Some(v.span);
-                                        }
-                                        _ => {}
-                                    }
-                                }
-                                None
-                            })
-                            .map(|var_span| (args_span, var_span))
-                    } else {
-                        None
-                    };
-                }
-            }
-        }
-
-        None
-    }
-
-    fn report_conflicting_borrow(
-        &mut self,
-        context: Context,
-        common_prefix: &Place<'tcx>,
-        (place, span): (&Place<'tcx>, Span),
-        gen_borrow_kind: BorrowKind,
-        issued_borrow: &BorrowData,
-        end_issued_loan_span: Option<Span>,
-    ) {
-        use self::prefixes::IsPrefixOf;
-
-        assert!(common_prefix.is_prefix_of(place));
-        assert!(common_prefix.is_prefix_of(&issued_borrow.place));
-
-        let issued_span = self.retrieve_borrow_span(issued_borrow);
-
-        let new_closure_span = self.find_closure_span(span, context.loc);
-        let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
-        let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location);
-        let issued_span = old_closure_span
-            .map(|(args, _)| args)
-            .unwrap_or(issued_span);
-
-        let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
-
-        // FIXME: supply non-"" `opt_via` when appropriate
-        let mut err = match (
-            gen_borrow_kind,
-            "immutable",
-            "mutable",
-            issued_borrow.kind,
-            "immutable",
-            "mutable",
-        ) {
-            (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
-            (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx
-                .cannot_reborrow_already_borrowed(
-                    span,
-                    &desc_place,
-                    "",
-                    lft,
-                    issued_span,
-                    "it",
-                    rgt,
-                    "",
-                    end_issued_loan_span,
-                    Origin::Mir,
-                ),
-
-            (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx
-                .cannot_mutably_borrow_multiply(
-                    span,
-                    &desc_place,
-                    "",
-                    issued_span,
-                    "",
-                    end_issued_loan_span,
-                    Origin::Mir,
-                ),
-
-            (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx
-                .cannot_uniquely_borrow_by_two_closures(
-                    span,
-                    &desc_place,
-                    issued_span,
-                    end_issued_loan_span,
-                    Origin::Mir,
-                ),
-
-            (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure(
-                span,
-                &desc_place,
-                "",
-                issued_span,
-                "it",
-                "",
-                end_issued_loan_span,
-                Origin::Mir,
-            ),
-
-            (_, _, _, BorrowKind::Unique, _, _) => self.tcx
-                .cannot_reborrow_already_uniquely_borrowed(
-                    span,
-                    &desc_place,
-                    "it",
-                    "",
-                    issued_span,
-                    "",
-                    end_issued_loan_span,
-                    Origin::Mir,
-                ),
-
-            (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
-        };
-
-        if let Some((_, var_span)) = old_closure_span {
-            err.span_label(
-                var_span,
-                format!(
-                    "previous borrow occurs due to use of `{}` in closure",
-                    desc_place
-                ),
-            );
-        }
-
-        if let Some((_, var_span)) = new_closure_span {
-            err.span_label(
-                var_span,
-                format!("borrow occurs due to use of `{}` in closure", desc_place),
-            );
-        }
-
-        err.emit();
-    }
-
-    fn report_borrowed_value_does_not_live_long_enough(
-        &mut self,
-        _: Context,
-        (place, span): (&Place<'tcx>, Span),
-        end_span: Option<Span>,
-    ) {
-        let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
-        let proper_span = match *root_place {
-            Place::Local(local) => self.mir.local_decls[local].source_info.span,
-            _ => span,
-        };
-        let mut err = self.tcx
-            .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
-        err.span_label(proper_span, "temporary value created here");
-        err.span_label(span, "temporary value dropped here while still borrowed");
-        err.note("consider using a `let` binding to increase its lifetime");
-
-        if let Some(end) = end_span {
-            err.span_label(end, "temporary value needs to live until here");
-        }
-
-        err.emit();
-    }
-
-    fn report_illegal_mutation_of_borrowed(
-        &mut self,
-        _: Context,
-        (place, span): (&Place<'tcx>, Span),
-        loan: &BorrowData,
-    ) {
-        let mut err = self.tcx.cannot_assign_to_borrowed(
-            span,
-            self.retrieve_borrow_span(loan),
-            &self.describe_place(place).unwrap_or("_".to_owned()),
-            Origin::Mir,
-        );
-
-        err.emit();
-    }
-
-    fn report_illegal_reassignment(
-        &mut self,
-        _context: Context,
-        (place, span): (&Place<'tcx>, Span),
-        assigned_span: Span,
-    ) {
-        let mut err = self.tcx.cannot_reassign_immutable(
-            span,
-            &self.describe_place(place).unwrap_or("_".to_owned()),
-            Origin::Mir,
-        );
-        err.span_label(span, "cannot assign twice to immutable variable");
-        if span != assigned_span {
-            let value_msg = match self.describe_place(place) {
-                Some(name) => format!("`{}`", name),
-                None => "value".to_owned(),
-            };
-            err.span_label(assigned_span, format!("first assignment to {}", value_msg));
-        }
-        err.emit();
-    }
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-    // End-user visible description of `place` if one can be found. If the
-    // place is a temporary for instance, None will be returned.
-    fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
-        let mut buf = String::new();
-        match self.append_place_to_string(place, &mut buf, false) {
-            Ok(()) => Some(buf),
-            Err(()) => None,
-        }
-    }
-
-    /// If this is a field projection, and the field is being projected from a closure type,
-    /// then returns the index of the field being projected. Note that this closure will always
-    /// be `self` in the current MIR, because that is the only time we directly access the fields
-    /// of a closure type.
-    fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
-        match *place {
-            Place::Projection(ref proj) => match proj.elem {
-                ProjectionElem::Field(field, _ty) => {
-                    let is_projection_from_ty_closure = proj.base
-                        .ty(self.mir, self.tcx)
-                        .to_ty(self.tcx)
-                        .is_closure();
-
-                    if is_projection_from_ty_closure {
-                        Some(field)
-                    } else {
-                        None
-                    }
-                }
-                _ => None,
-            },
-            _ => None,
-        }
-    }
-
-    // Appends end-user visible description of `place` to `buf`.
-    fn append_place_to_string(
-        &self,
-        place: &Place<'tcx>,
-        buf: &mut String,
-        mut autoderef: bool,
-    ) -> Result<(), ()> {
-        match *place {
-            Place::Local(local) => {
-                self.append_local_to_string(local, buf)?;
-            }
-            Place::Static(ref static_) => {
-                buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
-            }
-            Place::Projection(ref proj) => {
-                match proj.elem {
-                    ProjectionElem::Deref => {
-                        if let Some(field) = self.is_upvar_field_projection(&proj.base) {
-                            let var_index = field.index();
-                            let name = self.mir.upvar_decls[var_index].debug_name.to_string();
-                            if self.mir.upvar_decls[var_index].by_ref {
-                                buf.push_str(&name);
-                            } else {
-                                buf.push_str(&format!("*{}", &name));
-                            }
-                        } else {
-                            if autoderef {
-                                self.append_place_to_string(&proj.base, buf, autoderef)?;
-                            } else {
-                                buf.push_str(&"*");
-                                self.append_place_to_string(&proj.base, buf, autoderef)?;
-                            }
-                        }
-                    }
-                    ProjectionElem::Downcast(..) => {
-                        self.append_place_to_string(&proj.base, buf, autoderef)?;
-                    }
-                    ProjectionElem::Field(field, _ty) => {
-                        autoderef = true;
-
-                        if let Some(field) = self.is_upvar_field_projection(place) {
-                            let var_index = field.index();
-                            let name = self.mir.upvar_decls[var_index].debug_name.to_string();
-                            buf.push_str(&name);
-                        } else {
-                            let field_name = self.describe_field(&proj.base, field);
-                            self.append_place_to_string(&proj.base, buf, autoderef)?;
-                            buf.push_str(&format!(".{}", field_name));
-                        }
-                    }
-                    ProjectionElem::Index(index) => {
-                        autoderef = true;
-
-                        self.append_place_to_string(&proj.base, buf, autoderef)?;
-                        buf.push_str("[");
-                        if let Err(_) = self.append_local_to_string(index, buf) {
-                            buf.push_str("..");
-                        }
-                        buf.push_str("]");
-                    }
-                    ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
-                        autoderef = true;
-                        // Since it isn't possible to borrow an element on a particular index and
-                        // then use another while the borrow is held, don't output indices details
-                        // to avoid confusing the end-user
-                        self.append_place_to_string(&proj.base, buf, autoderef)?;
-                        buf.push_str(&"[..]");
-                    }
-                };
-            }
-        }
-
-        Ok(())
-    }
-
-    // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
-    // a name, then `Err` is returned
-    fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
-        let local = &self.mir.local_decls[local_index];
-        match local.name {
-            Some(name) => {
-                buf.push_str(&format!("{}", name));
-                Ok(())
-            }
-            None => Err(()),
-        }
-    }
-
-    // End-user visible description of the `field`nth field of `base`
-    fn describe_field(&self, base: &Place, field: Field) -> String {
-        match *base {
-            Place::Local(local) => {
-                let local = &self.mir.local_decls[local];
-                self.describe_field_from_ty(&local.ty, field)
-            }
-            Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
-            Place::Projection(ref proj) => match proj.elem {
-                ProjectionElem::Deref => self.describe_field(&proj.base, field),
-                ProjectionElem::Downcast(def, variant_index) => {
-                    format!("{}", def.variants[variant_index].fields[field.index()].name)
-                }
-                ProjectionElem::Field(_, field_type) => {
-                    self.describe_field_from_ty(&field_type, field)
-                }
-                ProjectionElem::Index(..) |
-                ProjectionElem::ConstantIndex { .. } |
-                ProjectionElem::Subslice { .. } => {
-                    format!("{}", self.describe_field(&proj.base, field))
-                }
-            },
-        }
-    }
-
-    // End-user visible description of the `field_index`nth field of `ty`
-    fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
-        if ty.is_box() {
-            // If the type is a box, the field is described from the boxed type
-            self.describe_field_from_ty(&ty.boxed_ty(), field)
-        } else {
-            match ty.sty {
-                ty::TyAdt(def, _) => if def.is_enum() {
-                    format!("{}", field.index())
-                } else {
-                    format!("{}", def.struct_variant().fields[field.index()].name)
-                },
-                ty::TyTuple(_, _) => format!("{}", field.index()),
-                ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => {
-                    self.describe_field_from_ty(&tnm.ty, field)
-                }
-                ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
-                ty::TyClosure(closure_def_id, _) => {
-                    // Convert the def-id into a node-id. node-ids are only valid for
-                    // the local code in the current crate, so this returns an `Option` in case
-                    // the closure comes from another crate. But in that case we wouldn't
-                    // be borrowck'ing it, so we can just unwrap:
-                    let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
-                    let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
-
-                    self.tcx.hir.name(freevar.var_id()).to_string()
-                }
-                _ => {
-                    // Might need a revision when the fields in trait RFC is implemented
-                    // (https://github.com/rust-lang/rfcs/pull/1546)
-                    bug!(
-                        "End-user description not implemented for field access on `{:?}`",
-                        ty.sty
-                    );
-                }
-            }
-        }
-    }
-
-    // Retrieve span of given borrow from the current MIR representation
-    fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
-        self.mir.source_info(borrow.location).span
-    }
-}
-
-impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
-    // FIXME (#16118): function intended to allow the borrow checker
-    // to be less precise in its handling of Box while still allowing
-    // moves out of a Box. They should be removed when/if we stop
-    // treating Box specially (e.g. when/if DerefMove is added...)
-
-    fn base_path<'d>(&self, place: &'d Place<'tcx>) -> &'d Place<'tcx> {
-        //! Returns the base of the leftmost (deepest) dereference of an
-        //! Box in `place`. If there is no dereference of an Box
-        //! in `place`, then it just returns `place` itself.
-
-        let mut cursor = place;
-        let mut deepest = place;
-        loop {
-            let proj = match *cursor {
-                Place::Local(..) | Place::Static(..) => return deepest,
-                Place::Projection(ref proj) => proj,
-            };
-            if proj.elem == ProjectionElem::Deref
-                && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box()
-            {
-                deepest = &proj.base;
-            }
-            cursor = &proj.base;
-        }
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-struct Context {
-    kind: ContextKind,
-    loc: Location,
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, Debug)]
-enum ContextKind {
-    AssignLhs,
-    AssignRhs,
-    SetDiscrim,
-    InlineAsm,
-    SwitchInt,
-    Drop,
-    DropAndReplace,
-    CallOperator,
-    CallOperand,
-    CallDest,
-    Assert,
-    Yield,
-    StorageDead,
-}
-
-impl ContextKind {
-    fn new(self, loc: Location) -> Context {
-        Context {
-            kind: self,
-            loc: loc,
-        }
-    }
-}
-
-impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
-    pub(super) fn new(
-        borrows: DataflowResults<Borrows<'b, 'gcx, 'tcx>>,
-        inits: DataflowResults<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
-        uninits: DataflowResults<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
-        move_out: DataflowResults<MovingOutStatements<'b, 'gcx, 'tcx>>,
-        ever_inits: DataflowResults<EverInitializedLvals<'b, 'gcx, 'tcx>>,
-    ) -> Self {
-        InProgress {
-            borrows: FlowInProgress::new(borrows),
-            inits: FlowInProgress::new(inits),
-            uninits: FlowInProgress::new(uninits),
-            move_outs: FlowInProgress::new(move_out),
-            ever_inits: FlowInProgress::new(ever_inits),
-        }
-    }
-
-    fn each_flow<XB, XI, XU, XM, XE>(
-        &mut self,
-        mut xform_borrows: XB,
-        mut xform_inits: XI,
-        mut xform_uninits: XU,
-        mut xform_move_outs: XM,
-        mut xform_ever_inits: XE,
-    ) where
-        XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
-        XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
-        XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
-        XM: FnMut(&mut FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>),
-        XE: FnMut(&mut FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>),
-    {
-        xform_borrows(&mut self.borrows);
-        xform_inits(&mut self.inits);
-        xform_uninits(&mut self.uninits);
-        xform_move_outs(&mut self.move_outs);
-        xform_ever_inits(&mut self.ever_inits);
-    }
-
-    fn summary(&self) -> String {
-        let mut s = String::new();
-
-        s.push_str("borrows in effect: [");
-        let mut saw_one = false;
-        self.borrows.each_state_bit(|borrow| {
-            if saw_one {
-                s.push_str(", ");
-            };
-            saw_one = true;
-            let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
-            s.push_str(&format!("{}", borrow_data));
-        });
-        s.push_str("] ");
-
-        s.push_str("borrows generated: [");
-        let mut saw_one = false;
-        self.borrows.each_gen_bit(|borrow| {
-            if saw_one {
-                s.push_str(", ");
-            };
-            saw_one = true;
-            let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
-            s.push_str(&format!("{}", borrow_data));
-        });
-        s.push_str("] ");
-
-        s.push_str("inits: [");
-        let mut saw_one = false;
-        self.inits.each_state_bit(|mpi_init| {
-            if saw_one {
-                s.push_str(", ");
-            };
-            saw_one = true;
-            let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init];
-            s.push_str(&format!("{}", move_path));
-        });
-        s.push_str("] ");
-
-        s.push_str("uninits: [");
-        let mut saw_one = false;
-        self.uninits.each_state_bit(|mpi_uninit| {
-            if saw_one {
-                s.push_str(", ");
-            };
-            saw_one = true;
-            let move_path =
-                &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit];
-            s.push_str(&format!("{}", move_path));
-        });
-        s.push_str("] ");
-
-        s.push_str("move_out: [");
-        let mut saw_one = false;
-        self.move_outs.each_state_bit(|mpi_move_out| {
-            if saw_one {
-                s.push_str(", ");
-            };
-            saw_one = true;
-            let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out];
-            s.push_str(&format!("{:?}", move_out));
-        });
-        s.push_str("] ");
-
-        s.push_str("ever_init: [");
-        let mut saw_one = false;
-        self.ever_inits.each_state_bit(|mpi_ever_init| {
-            if saw_one {
-                s.push_str(", ");
-            };
-            saw_one = true;
-            let ever_init =
-                &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init];
-            s.push_str(&format!("{:?}", ever_init));
-        });
-        s.push_str("]");
-
-        return s;
-    }
-}
-
-impl<'b, 'gcx, 'tcx> FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>> {
-    fn has_any_child_of(&self, mpi: MovePathIndex) -> Option<MovePathIndex> {
-        let move_data = self.base_results.operator().move_data();
-
-        let mut todo = vec![mpi];
-        let mut push_siblings = false; // don't look at siblings of original `mpi`.
-        while let Some(mpi) = todo.pop() {
-            if self.curr_state.contains(&mpi) {
-                return Some(mpi);
-            }
-            let move_path = &move_data.move_paths[mpi];
-            if let Some(child) = move_path.first_child {
-                todo.push(child);
-            }
-            if push_siblings {
-                if let Some(sibling) = move_path.next_sibling {
-                    todo.push(sibling);
-                }
-            } else {
-                // after we've processed the original `mpi`, we should
-                // always traverse the siblings of any of its
-                // children.
-                push_siblings = true;
-            }
-        }
-        return None;
-    }
-}
-
-impl<BD> FlowInProgress<BD>
-where
-    BD: BitDenotation,
-{
-    fn each_state_bit<F>(&self, f: F)
-    where
-        F: FnMut(BD::Idx),
-    {
-        self.curr_state
-            .each_bit(self.base_results.operator().bits_per_block(), f)
-    }
-
-    fn each_gen_bit<F>(&self, f: F)
-    where
-        F: FnMut(BD::Idx),
-    {
-        self.stmt_gen
-            .each_bit(self.base_results.operator().bits_per_block(), f)
-    }
-
-    fn new(results: DataflowResults<BD>) -> Self {
-        let bits_per_block = results.sets().bits_per_block();
-        let curr_state = IdxSetBuf::new_empty(bits_per_block);
-        let stmt_gen = IdxSetBuf::new_empty(bits_per_block);
-        let stmt_kill = IdxSetBuf::new_empty(bits_per_block);
-        FlowInProgress {
-            base_results: results,
-            curr_state: curr_state,
-            stmt_gen: stmt_gen,
-            stmt_kill: stmt_kill,
-        }
-    }
-
-    fn reset_to_entry_of(&mut self, bb: BasicBlock) {
-        (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index()));
-    }
-
-    fn reconstruct_statement_effect(&mut self, loc: Location) {
-        self.stmt_gen.reset_to_empty();
-        self.stmt_kill.reset_to_empty();
-        let mut ignored = IdxSetBuf::new_empty(0);
-        let mut sets = BlockSets {
-            on_entry: &mut ignored,
-            gen_set: &mut self.stmt_gen,
-            kill_set: &mut self.stmt_kill,
-        };
-        self.base_results
-            .operator()
-            .statement_effect(&mut sets, loc);
-    }
-
-    fn reconstruct_terminator_effect(&mut self, loc: Location) {
-        self.stmt_gen.reset_to_empty();
-        self.stmt_kill.reset_to_empty();
-        let mut ignored = IdxSetBuf::new_empty(0);
-        let mut sets = BlockSets {
-            on_entry: &mut ignored,
-            gen_set: &mut self.stmt_gen,
-            kill_set: &mut self.stmt_kill,
-        };
-        self.base_results
-            .operator()
-            .terminator_effect(&mut sets, loc);
-    }
-
-    fn apply_local_effect(&mut self) {
-        self.curr_state.union(&self.stmt_gen);
-        self.curr_state.subtract(&self.stmt_kill);
-    }
-
-    fn elems_incoming(&self) -> indexed_set::Elems<BD::Idx> {
-        let univ = self.base_results.sets().bits_per_block();
-        self.curr_state.elems(univ)
-    }
-
-    fn with_elems_outgoing<F>(&self, f: F)
-    where
-        F: FnOnce(indexed_set::Elems<BD::Idx>),
-    {
-        let mut curr_state = self.curr_state.clone();
-        curr_state.union(&self.stmt_gen);
-        curr_state.subtract(&self.stmt_kill);
-        let univ = self.base_results.sets().bits_per_block();
-        f(curr_state.elems(univ));
-    }
-}
diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs
new file mode 100644 (file)
index 0000000..446aba3
--- /dev/null
@@ -0,0 +1,2575 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! This query borrow-checks the MIR to (further) ensure it is not broken.
+
+use rustc::hir;
+use rustc::hir::def_id::DefId;
+use rustc::infer::InferCtxt;
+use rustc::ty::{self, ParamEnv, TyCtxt};
+use rustc::ty::maps::Providers;
+use rustc::mir::{AssertMessage, BasicBlock, BorrowKind, Local, Location, Place};
+use rustc::mir::{Mir, Mutability, Operand, Projection, ProjectionElem, Rvalue};
+use rustc::mir::{Field, Statement, StatementKind, Terminator, TerminatorKind};
+
+use rustc_data_structures::fx::FxHashSet;
+use rustc_data_structures::indexed_set::{self, IdxSetBuf};
+use rustc_data_structures::indexed_vec::Idx;
+
+use syntax::ast;
+use syntax_pos::Span;
+
+use dataflow::do_dataflow;
+use dataflow::MoveDataParamEnv;
+use dataflow::{BitDenotation, BlockSets, DataflowResults, DataflowResultsConsumer};
+use dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals};
+use dataflow::{EverInitializedLvals, MovingOutStatements};
+use dataflow::{BorrowData, BorrowIndex, Borrows};
+use dataflow::move_paths::{IllegalMoveOriginKind, MoveError};
+use dataflow::move_paths::{HasMoveData, LookupResult, MoveData, MoveOutIndex, MovePathIndex};
+use util::borrowck_errors::{BorrowckErrors, Origin};
+
+use self::MutateMode::{JustWrite, WriteAndRead};
+
+pub(crate) mod nll;
+
+pub fn provide(providers: &mut Providers) {
+    *providers = Providers {
+        mir_borrowck,
+        ..*providers
+    };
+}
+
+fn mir_borrowck<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) {
+    let input_mir = tcx.mir_validated(def_id);
+    debug!("run query mir_borrowck: {}", tcx.item_path_str(def_id));
+
+    if {
+        !tcx.has_attr(def_id, "rustc_mir_borrowck") && !tcx.sess.opts.borrowck_mode.use_mir()
+            && !tcx.sess.opts.debugging_opts.nll
+    } {
+        return;
+    }
+
+    tcx.infer_ctxt().enter(|infcx| {
+        let input_mir: &Mir = &input_mir.borrow();
+        do_mir_borrowck(&infcx, input_mir, def_id);
+    });
+    debug!("mir_borrowck done");
+}
+
+fn do_mir_borrowck<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    input_mir: &Mir<'gcx>,
+    def_id: DefId,
+) {
+    let tcx = infcx.tcx;
+    let attributes = tcx.get_attrs(def_id);
+    let param_env = tcx.param_env(def_id);
+    let id = tcx.hir
+        .as_local_node_id(def_id)
+        .expect("do_mir_borrowck: non-local DefId");
+
+    // Make our own copy of the MIR. This copy will be modified (in place) to
+    // contain non-lexical lifetimes. It will have a lifetime tied
+    // to the inference context.
+    let mut mir: Mir<'tcx> = input_mir.clone();
+    let free_regions = if !tcx.sess.opts.debugging_opts.nll {
+        None
+    } else {
+        let mir = &mut mir;
+
+        // Replace all regions with fresh inference variables.
+        Some(nll::replace_regions_in_mir(infcx, def_id, mir))
+    };
+    let mir = &mir;
+
+    let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) {
+        Ok(move_data) => move_data,
+        Err((move_data, move_errors)) => {
+            for move_error in move_errors {
+                let (span, kind): (Span, IllegalMoveOriginKind) = match move_error {
+                    MoveError::UnionMove { .. } => {
+                        unimplemented!("dont know how to report union move errors yet.")
+                    }
+                    MoveError::IllegalMove {
+                        cannot_move_out_of: o,
+                    } => (o.span, o.kind),
+                };
+                let origin = Origin::Mir;
+                let mut err = match kind {
+                    IllegalMoveOriginKind::Static => {
+                        tcx.cannot_move_out_of(span, "static item", origin)
+                    }
+                    IllegalMoveOriginKind::BorrowedContent => {
+                        tcx.cannot_move_out_of(span, "borrowed content", origin)
+                    }
+                    IllegalMoveOriginKind::InteriorOfTypeWithDestructor { container_ty: ty } => {
+                        tcx.cannot_move_out_of_interior_of_drop(span, ty, origin)
+                    }
+                    IllegalMoveOriginKind::InteriorOfSliceOrArray { ty, is_index } => {
+                        tcx.cannot_move_out_of_interior_noncopy(span, ty, is_index, origin)
+                    }
+                };
+                err.emit();
+            }
+            move_data
+        }
+    };
+
+    let mdpe = MoveDataParamEnv {
+        move_data: move_data,
+        param_env: param_env,
+    };
+    let dead_unwinds = IdxSetBuf::new_empty(mir.basic_blocks().len());
+    let mut flow_inits = FlowInProgress::new(do_dataflow(
+        tcx,
+        mir,
+        id,
+        &attributes,
+        &dead_unwinds,
+        MaybeInitializedLvals::new(tcx, mir, &mdpe),
+        |bd, i| &bd.move_data().move_paths[i],
+    ));
+    let flow_uninits = FlowInProgress::new(do_dataflow(
+        tcx,
+        mir,
+        id,
+        &attributes,
+        &dead_unwinds,
+        MaybeUninitializedLvals::new(tcx, mir, &mdpe),
+        |bd, i| &bd.move_data().move_paths[i],
+    ));
+    let flow_move_outs = FlowInProgress::new(do_dataflow(
+        tcx,
+        mir,
+        id,
+        &attributes,
+        &dead_unwinds,
+        MovingOutStatements::new(tcx, mir, &mdpe),
+        |bd, i| &bd.move_data().moves[i],
+    ));
+    let flow_ever_inits = FlowInProgress::new(do_dataflow(
+        tcx,
+        mir,
+        id,
+        &attributes,
+        &dead_unwinds,
+        EverInitializedLvals::new(tcx, mir, &mdpe),
+        |bd, i| &bd.move_data().inits[i],
+    ));
+
+    // If we are in non-lexical mode, compute the non-lexical lifetimes.
+    let opt_regioncx = if let Some(free_regions) = free_regions {
+        Some(nll::compute_regions(
+            infcx,
+            def_id,
+            free_regions,
+            mir,
+            param_env,
+            &mut flow_inits,
+            &mdpe.move_data,
+        ))
+    } else {
+        assert!(!tcx.sess.opts.debugging_opts.nll);
+        None
+    };
+    let flow_inits = flow_inits; // remove mut
+
+    let mut mbcx = MirBorrowckCtxt {
+        tcx: tcx,
+        mir: mir,
+        node_id: id,
+        move_data: &mdpe.move_data,
+        param_env: param_env,
+        storage_dead_or_drop_error_reported: FxHashSet(),
+    };
+
+    let flow_borrows = FlowInProgress::new(do_dataflow(
+        tcx,
+        mir,
+        id,
+        &attributes,
+        &dead_unwinds,
+        Borrows::new(tcx, mir, opt_regioncx),
+        |bd, i| bd.location(i),
+    ));
+
+    let mut state = InProgress::new(
+        flow_borrows,
+        flow_inits,
+        flow_uninits,
+        flow_move_outs,
+        flow_ever_inits,
+    );
+
+    mbcx.analyze_results(&mut state); // entry point for DataflowResultsConsumer
+}
+
+#[allow(dead_code)]
+pub struct MirBorrowckCtxt<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+    tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+    mir: &'cx Mir<'tcx>,
+    node_id: ast::NodeId,
+    move_data: &'cx MoveData<'tcx>,
+    param_env: ParamEnv<'gcx>,
+    /// This field keeps track of when storage dead or drop errors are reported
+    /// in order to stop duplicate error reporting and identify the conditions required
+    /// for a "temporary value dropped here while still borrowed" error. See #45360.
+    storage_dead_or_drop_error_reported: FxHashSet<Local>,
+}
+
+// (forced to be `pub` due to its use as an associated type below.)
+pub struct InProgress<'b, 'gcx: 'tcx, 'tcx: 'b> {
+    borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
+    inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
+    uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
+    move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
+    ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
+}
+
+struct FlowInProgress<BD>
+where
+    BD: BitDenotation,
+{
+    base_results: DataflowResults<BD>,
+    curr_state: IdxSetBuf<BD::Idx>,
+    stmt_gen: IdxSetBuf<BD::Idx>,
+    stmt_kill: IdxSetBuf<BD::Idx>,
+}
+
+// Check that:
+// 1. assignments are always made to mutable locations (FIXME: does that still really go here?)
+// 2. loans made in overlapping scopes do not conflict
+// 3. assignments do not affect things loaned out as immutable
+// 4. moves do not affect things loaned out in any way
+impl<'cx, 'gcx, 'tcx> DataflowResultsConsumer<'cx, 'tcx> for MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    type FlowState = InProgress<'cx, 'gcx, 'tcx>;
+
+    fn mir(&self) -> &'cx Mir<'tcx> {
+        self.mir
+    }
+
+    fn reset_to_entry_of(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
+        flow_state.each_flow(
+            |b| b.reset_to_entry_of(bb),
+            |i| i.reset_to_entry_of(bb),
+            |u| u.reset_to_entry_of(bb),
+            |m| m.reset_to_entry_of(bb),
+            |e| e.reset_to_entry_of(bb),
+        );
+    }
+
+    fn reconstruct_statement_effect(
+        &mut self,
+        location: Location,
+        flow_state: &mut Self::FlowState,
+    ) {
+        flow_state.each_flow(
+            |b| b.reconstruct_statement_effect(location),
+            |i| i.reconstruct_statement_effect(location),
+            |u| u.reconstruct_statement_effect(location),
+            |m| m.reconstruct_statement_effect(location),
+            |e| e.reconstruct_statement_effect(location),
+        );
+    }
+
+    fn apply_local_effect(&mut self, _location: Location, flow_state: &mut Self::FlowState) {
+        flow_state.each_flow(
+            |b| b.apply_local_effect(),
+            |i| i.apply_local_effect(),
+            |u| u.apply_local_effect(),
+            |m| m.apply_local_effect(),
+            |e| e.apply_local_effect(),
+        );
+    }
+
+    fn reconstruct_terminator_effect(
+        &mut self,
+        location: Location,
+        flow_state: &mut Self::FlowState,
+    ) {
+        flow_state.each_flow(
+            |b| b.reconstruct_terminator_effect(location),
+            |i| i.reconstruct_terminator_effect(location),
+            |u| u.reconstruct_terminator_effect(location),
+            |m| m.reconstruct_terminator_effect(location),
+            |e| e.reconstruct_terminator_effect(location),
+        );
+    }
+
+    fn visit_block_entry(&mut self, bb: BasicBlock, flow_state: &Self::FlowState) {
+        let summary = flow_state.summary();
+        debug!("MirBorrowckCtxt::process_block({:?}): {}", bb, summary);
+    }
+
+    fn visit_statement_entry(
+        &mut self,
+        location: Location,
+        stmt: &Statement<'tcx>,
+        flow_state: &Self::FlowState,
+    ) {
+        let summary = flow_state.summary();
+        debug!(
+            "MirBorrowckCtxt::process_statement({:?}, {:?}): {}",
+            location,
+            stmt,
+            summary
+        );
+        let span = stmt.source_info.span;
+        match stmt.kind {
+            StatementKind::Assign(ref lhs, ref rhs) => {
+                // NOTE: NLL RFC calls for *shallow* write; using Deep
+                // for short-term compat w/ AST-borrowck. Also, switch
+                // to shallow requires to dataflow: "if this is an
+                // assignment `place = <rvalue>`, then any loan for some
+                // path P of which `place` is a prefix is killed."
+                self.mutate_place(
+                    ContextKind::AssignLhs.new(location),
+                    (lhs, span),
+                    Deep,
+                    JustWrite,
+                    flow_state,
+                );
+
+                self.consume_rvalue(
+                    ContextKind::AssignRhs.new(location),
+                    (rhs, span),
+                    location,
+                    flow_state,
+                );
+            }
+            StatementKind::SetDiscriminant {
+                ref place,
+                variant_index: _,
+            } => {
+                self.mutate_place(
+                    ContextKind::SetDiscrim.new(location),
+                    (place, span),
+                    Shallow(Some(ArtificialField::Discriminant)),
+                    JustWrite,
+                    flow_state,
+                );
+            }
+            StatementKind::InlineAsm {
+                ref asm,
+                ref outputs,
+                ref inputs,
+            } => {
+                let context = ContextKind::InlineAsm.new(location);
+                for (o, output) in asm.outputs.iter().zip(outputs) {
+                    if o.is_indirect {
+                        // FIXME(eddyb) indirect inline asm outputs should
+                        // be encoeded through MIR place derefs instead.
+                        self.access_place(
+                            context,
+                            (output, span),
+                            (Deep, Read(ReadKind::Copy)),
+                            LocalMutationIsAllowed::No,
+                            flow_state,
+                        );
+                        self.check_if_path_is_moved(
+                            context,
+                            InitializationRequiringAction::Use,
+                            (output, span),
+                            flow_state,
+                        );
+                    } else {
+                        self.mutate_place(
+                            context,
+                            (output, span),
+                            Deep,
+                            if o.is_rw { WriteAndRead } else { JustWrite },
+                            flow_state,
+                        );
+                    }
+                }
+                for input in inputs {
+                    self.consume_operand(context, (input, span), flow_state);
+                }
+            }
+            StatementKind::EndRegion(ref _rgn) => {
+                // ignored when consuming results (update to
+                // flow_state already handled).
+            }
+            StatementKind::Nop | StatementKind::Validate(..) | StatementKind::StorageLive(..) => {
+                // `Nop`, `Validate`, and `StorageLive` are irrelevant
+                // to borrow check.
+            }
+
+            StatementKind::StorageDead(local) => {
+                self.access_place(
+                    ContextKind::StorageDead.new(location),
+                    (&Place::Local(local), span),
+                    (Shallow(None), Write(WriteKind::StorageDeadOrDrop)),
+                    LocalMutationIsAllowed::Yes,
+                    flow_state,
+                );
+            }
+        }
+    }
+
+    fn visit_terminator_entry(
+        &mut self,
+        location: Location,
+        term: &Terminator<'tcx>,
+        flow_state: &Self::FlowState,
+    ) {
+        let loc = location;
+        let summary = flow_state.summary();
+        debug!(
+            "MirBorrowckCtxt::process_terminator({:?}, {:?}): {}",
+            location,
+            term,
+            summary
+        );
+        let span = term.source_info.span;
+        match term.kind {
+            TerminatorKind::SwitchInt {
+                ref discr,
+                switch_ty: _,
+                values: _,
+                targets: _,
+            } => {
+                self.consume_operand(ContextKind::SwitchInt.new(loc), (discr, span), flow_state);
+            }
+            TerminatorKind::Drop {
+                location: ref drop_place,
+                target: _,
+                unwind: _,
+            } => {
+                self.access_place(
+                    ContextKind::Drop.new(loc),
+                    (drop_place, span),
+                    (Deep, Write(WriteKind::StorageDeadOrDrop)),
+                    LocalMutationIsAllowed::Yes,
+                    flow_state,
+                );
+            }
+            TerminatorKind::DropAndReplace {
+                location: ref drop_place,
+                value: ref new_value,
+                target: _,
+                unwind: _,
+            } => {
+                self.mutate_place(
+                    ContextKind::DropAndReplace.new(loc),
+                    (drop_place, span),
+                    Deep,
+                    JustWrite,
+                    flow_state,
+                );
+                self.consume_operand(
+                    ContextKind::DropAndReplace.new(loc),
+                    (new_value, span),
+                    flow_state,
+                );
+            }
+            TerminatorKind::Call {
+                ref func,
+                ref args,
+                ref destination,
+                cleanup: _,
+            } => {
+                self.consume_operand(ContextKind::CallOperator.new(loc), (func, span), flow_state);
+                for arg in args {
+                    self.consume_operand(
+                        ContextKind::CallOperand.new(loc),
+                        (arg, span),
+                        flow_state,
+                    );
+                }
+                if let Some((ref dest, _ /*bb*/)) = *destination {
+                    self.mutate_place(
+                        ContextKind::CallDest.new(loc),
+                        (dest, span),
+                        Deep,
+                        JustWrite,
+                        flow_state,
+                    );
+                }
+            }
+            TerminatorKind::Assert {
+                ref cond,
+                expected: _,
+                ref msg,
+                target: _,
+                cleanup: _,
+            } => {
+                self.consume_operand(ContextKind::Assert.new(loc), (cond, span), flow_state);
+                match *msg {
+                    AssertMessage::BoundsCheck { ref len, ref index } => {
+                        self.consume_operand(ContextKind::Assert.new(loc), (len, span), flow_state);
+                        self.consume_operand(
+                            ContextKind::Assert.new(loc),
+                            (index, span),
+                            flow_state,
+                        );
+                    }
+                    AssertMessage::Math(_ /*const_math_err*/) => {}
+                    AssertMessage::GeneratorResumedAfterReturn => {}
+                    AssertMessage::GeneratorResumedAfterPanic => {}
+                }
+            }
+
+            TerminatorKind::Yield {
+                ref value,
+                resume: _,
+                drop: _,
+            } => {
+                self.consume_operand(ContextKind::Yield.new(loc), (value, span), flow_state);
+            }
+
+            TerminatorKind::Resume | TerminatorKind::Return | TerminatorKind::GeneratorDrop => {
+                // Returning from the function implicitly kills storage for all locals and statics.
+                // Often, the storage will already have been killed by an explicit
+                // StorageDead, but we don't always emit those (notably on unwind paths),
+                // so this "extra check" serves as a kind of backup.
+                let domain = flow_state.borrows.base_results.operator();
+                let data = domain.borrows();
+                flow_state.borrows.with_elems_outgoing(|borrows| {
+                    for i in borrows {
+                        let borrow = &data[i];
+
+                        if self.place_is_invalidated_at_exit(&borrow.place) {
+                            debug!("borrow conflicts at exit {:?}", borrow);
+                            let borrow_span = self.mir.source_info(borrow.location).span;
+                            // FIXME: should be talking about the region lifetime instead
+                            // of just a span here.
+                            let end_span = domain.opt_region_end_span(&borrow.region);
+
+                            self.report_borrowed_value_does_not_live_long_enough(
+                                ContextKind::StorageDead.new(loc),
+                                (&borrow.place, borrow_span),
+                                end_span,
+                            )
+                        }
+                    }
+                });
+            }
+            TerminatorKind::Goto { target: _ } |
+            TerminatorKind::Unreachable |
+            TerminatorKind::FalseEdges { .. } => {
+                // no data used, thus irrelevant to borrowck
+            }
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum MutateMode {
+    JustWrite,
+    WriteAndRead,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum Control {
+    Continue,
+    Break,
+}
+
+use self::ShallowOrDeep::{Deep, Shallow};
+use self::ReadOrWrite::{Read, Write};
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ArtificialField {
+    Discriminant,
+    ArrayLength,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ShallowOrDeep {
+    /// From the RFC: "A *shallow* access means that the immediate
+    /// fields reached at LV are accessed, but references or pointers
+    /// found within are not dereferenced. Right now, the only access
+    /// that is shallow is an assignment like `x = ...;`, which would
+    /// be a *shallow write* of `x`."
+    Shallow(Option<ArtificialField>),
+
+    /// From the RFC: "A *deep* access means that all data reachable
+    /// through the given place may be invalidated or accesses by
+    /// this action."
+    Deep,
+}
+
+/// Kind of access to a value: read or write
+/// (For informational purposes only)
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ReadOrWrite {
+    /// From the RFC: "A *read* means that the existing data may be
+    /// read, but will not be changed."
+    Read(ReadKind),
+
+    /// From the RFC: "A *write* means that the data may be mutated to
+    /// new values or otherwise invalidated (for example, it could be
+    /// de-initialized, as in a move operation).
+    Write(WriteKind),
+}
+
+/// Kind of read access to a value
+/// (For informational purposes only)
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ReadKind {
+    Borrow(BorrowKind),
+    Copy,
+}
+
+/// Kind of write access to a value
+/// (For informational purposes only)
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum WriteKind {
+    StorageDeadOrDrop,
+    MutableBorrow(BorrowKind),
+    Mutate,
+    Move,
+}
+
+/// When checking permissions for a place access, this flag is used to indicate that an immutable
+/// local place can be mutated.
+///
+/// FIXME: @nikomatsakis suggested that this flag could be removed with the following modifications:
+/// - Merge `check_access_permissions()` and `check_if_reassignment_to_immutable_state()`
+/// - Split `is_mutable()` into `is_assignable()` (can be directly assigned) and
+///   `is_declared_mutable()`
+/// - Take flow state into consideration in `is_assignable()` for local variables
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum LocalMutationIsAllowed {
+    Yes,
+    No,
+}
+
+#[derive(Copy, Clone)]
+enum InitializationRequiringAction {
+    Update,
+    Borrow,
+    Use,
+    Assignment,
+}
+
+impl InitializationRequiringAction {
+    fn as_noun(self) -> &'static str {
+        match self {
+            InitializationRequiringAction::Update => "update",
+            InitializationRequiringAction::Borrow => "borrow",
+            InitializationRequiringAction::Use => "use",
+            InitializationRequiringAction::Assignment => "assign",
+        }
+    }
+
+    fn as_verb_in_past_tense(self) -> &'static str {
+        match self {
+            InitializationRequiringAction::Update => "updated",
+            InitializationRequiringAction::Borrow => "borrowed",
+            InitializationRequiringAction::Use => "used",
+            InitializationRequiringAction::Assignment => "assigned",
+        }
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    /// Checks an access to the given place to see if it is allowed. Examines the set of borrows
+    /// that are in scope, as well as which paths have been initialized, to ensure that (a) the
+    /// place is initialized and (b) it is not borrowed in some way that would prevent this
+    /// access.
+    ///
+    /// Returns true if an error is reported, false otherwise.
+    fn access_place(
+        &mut self,
+        context: Context,
+        place_span: (&Place<'tcx>, Span),
+        kind: (ShallowOrDeep, ReadOrWrite),
+        is_local_mutation_allowed: LocalMutationIsAllowed,
+        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
+    ) {
+        let (sd, rw) = kind;
+
+        let storage_dead_or_drop_local = match (place_span.0, rw) {
+            (&Place::Local(local), Write(WriteKind::StorageDeadOrDrop)) => Some(local),
+            _ => None,
+        };
+
+        // Check if error has already been reported to stop duplicate reporting.
+        if let Some(local) = storage_dead_or_drop_local {
+            if self.storage_dead_or_drop_error_reported.contains(&local) {
+                return;
+            }
+        }
+
+        // Check permissions
+        let mut error_reported =
+            self.check_access_permissions(place_span, rw, is_local_mutation_allowed);
+
+        self.each_borrow_involving_path(
+            context,
+            (sd, place_span.0),
+            flow_state,
+            |this, _index, borrow, common_prefix| match (rw, borrow.kind) {
+                (Read(_), BorrowKind::Shared) => Control::Continue,
+                (Read(kind), BorrowKind::Unique) | (Read(kind), BorrowKind::Mut) => {
+                    match kind {
+                        ReadKind::Copy => {
+                            error_reported = true;
+                            this.report_use_while_mutably_borrowed(context, place_span, borrow)
+                        }
+                        ReadKind::Borrow(bk) => {
+                            let end_issued_loan_span = flow_state
+                                .borrows
+                                .base_results
+                                .operator()
+                                .opt_region_end_span(&borrow.region);
+                            error_reported = true;
+                            this.report_conflicting_borrow(
+                                context,
+                                common_prefix,
+                                place_span,
+                                bk,
+                                &borrow,
+                                end_issued_loan_span,
+                            )
+                        }
+                    }
+                    Control::Break
+                }
+                (Write(kind), _) => {
+                    match kind {
+                        WriteKind::MutableBorrow(bk) => {
+                            let end_issued_loan_span = flow_state
+                                .borrows
+                                .base_results
+                                .operator()
+                                .opt_region_end_span(&borrow.region);
+                            error_reported = true;
+                            this.report_conflicting_borrow(
+                                context,
+                                common_prefix,
+                                place_span,
+                                bk,
+                                &borrow,
+                                end_issued_loan_span,
+                            )
+                        }
+                        WriteKind::StorageDeadOrDrop => {
+                            let end_span = flow_state
+                                .borrows
+                                .base_results
+                                .operator()
+                                .opt_region_end_span(&borrow.region);
+                            error_reported = true;
+                            this.report_borrowed_value_does_not_live_long_enough(
+                                context,
+                                place_span,
+                                end_span,
+                            )
+                        }
+                        WriteKind::Mutate => {
+                            error_reported = true;
+                            this.report_illegal_mutation_of_borrowed(context, place_span, borrow)
+                        }
+                        WriteKind::Move => {
+                            error_reported = true;
+                            this.report_move_out_while_borrowed(context, place_span, &borrow)
+                        }
+                    }
+                    Control::Break
+                }
+            },
+        );
+
+        if error_reported {
+            if let Some(local) = storage_dead_or_drop_local {
+                self.storage_dead_or_drop_error_reported.insert(local);
+            }
+        }
+    }
+
+    fn mutate_place(
+        &mut self,
+        context: Context,
+        place_span: (&Place<'tcx>, Span),
+        kind: ShallowOrDeep,
+        mode: MutateMode,
+        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
+    ) {
+        // Write of P[i] or *P, or WriteAndRead of any P, requires P init'd.
+        match mode {
+            MutateMode::WriteAndRead => {
+                self.check_if_path_is_moved(
+                    context,
+                    InitializationRequiringAction::Update,
+                    place_span,
+                    flow_state,
+                );
+            }
+            MutateMode::JustWrite => {
+                self.check_if_assigned_path_is_moved(context, place_span, flow_state);
+            }
+        }
+
+        self.access_place(
+            context,
+            place_span,
+            (kind, Write(WriteKind::Mutate)),
+            LocalMutationIsAllowed::Yes,
+            flow_state,
+        );
+
+        // check for reassignments to immutable local variables
+        self.check_if_reassignment_to_immutable_state(context, place_span, flow_state);
+    }
+
+    fn consume_rvalue(
+        &mut self,
+        context: Context,
+        (rvalue, span): (&Rvalue<'tcx>, Span),
+        _location: Location,
+        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
+    ) {
+        match *rvalue {
+            Rvalue::Ref(_ /*rgn*/, bk, ref place) => {
+                let access_kind = match bk {
+                    BorrowKind::Shared => (Deep, Read(ReadKind::Borrow(bk))),
+                    BorrowKind::Unique | BorrowKind::Mut => {
+                        (Deep, Write(WriteKind::MutableBorrow(bk)))
+                    }
+                };
+                self.access_place(
+                    context,
+                    (place, span),
+                    access_kind,
+                    LocalMutationIsAllowed::No,
+                    flow_state,
+                );
+                self.check_if_path_is_moved(
+                    context,
+                    InitializationRequiringAction::Borrow,
+                    (place, span),
+                    flow_state,
+                );
+            }
+
+            Rvalue::Use(ref operand) |
+            Rvalue::Repeat(ref operand, _) |
+            Rvalue::UnaryOp(_ /*un_op*/, ref operand) |
+            Rvalue::Cast(_ /*cast_kind*/, ref operand, _ /*ty*/) => {
+                self.consume_operand(context, (operand, span), flow_state)
+            }
+
+            Rvalue::Len(ref place) | Rvalue::Discriminant(ref place) => {
+                let af = match *rvalue {
+                    Rvalue::Len(..) => ArtificialField::ArrayLength,
+                    Rvalue::Discriminant(..) => ArtificialField::Discriminant,
+                    _ => unreachable!(),
+                };
+                self.access_place(
+                    context,
+                    (place, span),
+                    (Shallow(Some(af)), Read(ReadKind::Copy)),
+                    LocalMutationIsAllowed::No,
+                    flow_state,
+                );
+                self.check_if_path_is_moved(
+                    context,
+                    InitializationRequiringAction::Use,
+                    (place, span),
+                    flow_state,
+                );
+            }
+
+            Rvalue::BinaryOp(_bin_op, ref operand1, ref operand2) |
+            Rvalue::CheckedBinaryOp(_bin_op, ref operand1, ref operand2) => {
+                self.consume_operand(context, (operand1, span), flow_state);
+                self.consume_operand(context, (operand2, span), flow_state);
+            }
+
+            Rvalue::NullaryOp(_op, _ty) => {
+                // nullary ops take no dynamic input; no borrowck effect.
+                //
+                // FIXME: is above actually true? Do we want to track
+                // the fact that uninitialized data can be created via
+                // `NullOp::Box`?
+            }
+
+            Rvalue::Aggregate(ref _aggregate_kind, ref operands) => for operand in operands {
+                self.consume_operand(context, (operand, span), flow_state);
+            },
+        }
+    }
+
+    fn consume_operand(
+        &mut self,
+        context: Context,
+        (operand, span): (&Operand<'tcx>, Span),
+        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
+    ) {
+        match *operand {
+            Operand::Copy(ref place) => {
+                // copy of place: check if this is "copy of frozen path"
+                // (FIXME: see check_loans.rs)
+                self.access_place(
+                    context,
+                    (place, span),
+                    (Deep, Read(ReadKind::Copy)),
+                    LocalMutationIsAllowed::No,
+                    flow_state,
+                );
+
+                // Finally, check if path was already moved.
+                self.check_if_path_is_moved(
+                    context,
+                    InitializationRequiringAction::Use,
+                    (place, span),
+                    flow_state,
+                );
+            }
+            Operand::Move(ref place) => {
+                // move of place: check if this is move of already borrowed path
+                self.access_place(
+                    context,
+                    (place, span),
+                    (Deep, Write(WriteKind::Move)),
+                    LocalMutationIsAllowed::Yes,
+                    flow_state,
+                );
+
+                // Finally, check if path was already moved.
+                self.check_if_path_is_moved(
+                    context,
+                    InitializationRequiringAction::Use,
+                    (place, span),
+                    flow_state,
+                );
+            }
+            Operand::Constant(_) => {}
+        }
+    }
+
+    /// Returns whether a borrow of this place is invalidated when the function
+    /// exits
+    fn place_is_invalidated_at_exit(&self, place: &Place<'tcx>) -> bool {
+        debug!("place_is_invalidated_at_exit({:?})", place);
+        let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
+
+        // FIXME(nll-rfc#40): do more precise destructor tracking here. For now
+        // we just know that all locals are dropped at function exit (otherwise
+        // we'll have a memory leak) and assume that all statics have a destructor.
+        let (might_be_alive, will_be_dropped) = match root_place {
+            Place::Static(statik) => {
+                // Thread-locals might be dropped after the function exits, but
+                // "true" statics will never be.
+                let is_thread_local = self.tcx
+                    .get_attrs(statik.def_id)
+                    .iter()
+                    .any(|attr| attr.check_name("thread_local"));
+
+                (true, is_thread_local)
+            }
+            Place::Local(_) => {
+                // Locals are always dropped at function exit, and if they
+                // have a destructor it would've been called already.
+                (false, true)
+            }
+            Place::Projection(..) => {
+                bug!("root of {:?} is a projection ({:?})?", place, root_place)
+            }
+        };
+
+        if !will_be_dropped {
+            debug!(
+                "place_is_invalidated_at_exit({:?}) - won't be dropped",
+                place
+            );
+            return false;
+        }
+
+        // FIXME: replace this with a proper borrow_conflicts_with_place when
+        // that is merged.
+        let prefix_set = if might_be_alive {
+            PrefixSet::Supporting
+        } else {
+            PrefixSet::Shallow
+        };
+
+        self.prefixes(place, prefix_set)
+            .any(|prefix| prefix == root_place)
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    fn check_if_reassignment_to_immutable_state(
+        &mut self,
+        context: Context,
+        (place, span): (&Place<'tcx>, Span),
+        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
+    ) {
+        let move_data = self.move_data;
+
+        // determine if this path has a non-mut owner (and thus needs checking).
+        if let Ok(()) = self.is_mutable(place, LocalMutationIsAllowed::No) {
+            return;
+        }
+
+        if let Err(_) = self.is_mutable(place, LocalMutationIsAllowed::Yes) {
+            return;
+        }
+
+        match self.move_path_closest_to(place) {
+            Ok(mpi) => for ii in &move_data.init_path_map[mpi] {
+                if flow_state.ever_inits.curr_state.contains(ii) {
+                    let first_assign_span = self.move_data.inits[*ii].span;
+                    self.report_illegal_reassignment(context, (place, span), first_assign_span);
+                    break;
+                }
+            },
+            Err(NoMovePathFound::ReachedStatic) => {
+                let item_msg = match self.describe_place(place) {
+                    Some(name) => format!("immutable static item `{}`", name),
+                    None => "immutable static item".to_owned(),
+                };
+                self.tcx.sess.delay_span_bug(
+                    span,
+                    &format!(
+                        "cannot assign to {}, should have been caught by \
+                         `check_access_permissions()`",
+                        item_msg
+                    ),
+                );
+            }
+        }
+    }
+
+    fn check_if_path_is_moved(
+        &mut self,
+        context: Context,
+        desired_action: InitializationRequiringAction,
+        place_span: (&Place<'tcx>, Span),
+        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
+    ) {
+        // FIXME: analogous code in check_loans first maps `place` to
+        // its base_path ... but is that what we want here?
+        let place = self.base_path(place_span.0);
+
+        let maybe_uninits = &flow_state.uninits;
+        let curr_move_outs = &flow_state.move_outs.curr_state;
+
+        // Bad scenarios:
+        //
+        // 1. Move of `a.b.c`, use of `a.b.c`
+        // 2. Move of `a.b.c`, use of `a.b.c.d` (without first reinitializing `a.b.c.d`)
+        // 3. Move of `a.b.c`, use of `a` or `a.b`
+        // 4. Uninitialized `(a.b.c: &_)`, use of `*a.b.c`; note that with
+        //    partial initialization support, one might have `a.x`
+        //    initialized but not `a.b`.
+        //
+        // OK scenarios:
+        //
+        // 5. Move of `a.b.c`, use of `a.b.d`
+        // 6. Uninitialized `a.x`, initialized `a.b`, use of `a.b`
+        // 7. Copied `(a.b: &_)`, use of `*(a.b).c`; note that `a.b`
+        //    must have been initialized for the use to be sound.
+        // 8. Move of `a.b.c` then reinit of `a.b.c.d`, use of `a.b.c.d`
+
+        // The dataflow tracks shallow prefixes distinctly (that is,
+        // field-accesses on P distinctly from P itself), in order to
+        // track substructure initialization separately from the whole
+        // structure.
+        //
+        // E.g., when looking at (*a.b.c).d, if the closest prefix for
+        // which we have a MovePath is `a.b`, then that means that the
+        // initialization state of `a.b` is all we need to inspect to
+        // know if `a.b.c` is valid (and from that we infer that the
+        // dereference and `.d` access is also valid, since we assume
+        // `a.b.c` is assigned a reference to a initialized and
+        // well-formed record structure.)
+
+        // Therefore, if we seek out the *closest* prefix for which we
+        // have a MovePath, that should capture the initialization
+        // state for the place scenario.
+        //
+        // This code covers scenarios 1, 2, and 4.
+
+        debug!("check_if_path_is_moved part1 place: {:?}", place);
+        match self.move_path_closest_to(place) {
+            Ok(mpi) => {
+                if maybe_uninits.curr_state.contains(&mpi) {
+                    self.report_use_of_moved_or_uninitialized(
+                        context,
+                        desired_action,
+                        place_span,
+                        mpi,
+                        curr_move_outs,
+                    );
+                    return; // don't bother finding other problems.
+                }
+            }
+            Err(NoMovePathFound::ReachedStatic) => {
+                // Okay: we do not build MoveData for static variables
+            } // Only query longest prefix with a MovePath, not further
+              // ancestors; dataflow recurs on children when parents
+              // move (to support partial (re)inits).
+              //
+              // (I.e. querying parents breaks scenario 8; but may want
+              // to do such a query based on partial-init feature-gate.)
+        }
+
+        // A move of any shallow suffix of `place` also interferes
+        // with an attempt to use `place`. This is scenario 3 above.
+        //
+        // (Distinct from handling of scenarios 1+2+4 above because
+        // `place` does not interfere with suffixes of its prefixes,
+        // e.g. `a.b.c` does not interfere with `a.b.d`)
+
+        debug!("check_if_path_is_moved part2 place: {:?}", place);
+        if let Some(mpi) = self.move_path_for_place(place) {
+            if let Some(child_mpi) = maybe_uninits.has_any_child_of(mpi) {
+                self.report_use_of_moved_or_uninitialized(
+                    context,
+                    desired_action,
+                    place_span,
+                    child_mpi,
+                    curr_move_outs,
+                );
+                return; // don't bother finding other problems.
+            }
+        }
+    }
+
+    /// Currently MoveData does not store entries for all places in
+    /// the input MIR. For example it will currently filter out
+    /// places that are Copy; thus we do not track places of shared
+    /// reference type. This routine will walk up a place along its
+    /// prefixes, searching for a foundational place that *is*
+    /// tracked in the MoveData.
+    ///
+    /// An Err result includes a tag indicated why the search failed.
+    /// Currenly this can only occur if the place is built off of a
+    /// static variable, as we do not track those in the MoveData.
+    fn move_path_closest_to(
+        &mut self,
+        place: &Place<'tcx>,
+    ) -> Result<MovePathIndex, NoMovePathFound> {
+        let mut last_prefix = place;
+        for prefix in self.prefixes(place, PrefixSet::All) {
+            if let Some(mpi) = self.move_path_for_place(prefix) {
+                return Ok(mpi);
+            }
+            last_prefix = prefix;
+        }
+        match *last_prefix {
+            Place::Local(_) => panic!("should have move path for every Local"),
+            Place::Projection(_) => panic!("PrefixSet::All meant dont stop for Projection"),
+            Place::Static(_) => return Err(NoMovePathFound::ReachedStatic),
+        }
+    }
+
+    fn move_path_for_place(&mut self, place: &Place<'tcx>) -> Option<MovePathIndex> {
+        // If returns None, then there is no move path corresponding
+        // to a direct owner of `place` (which means there is nothing
+        // that borrowck tracks for its analysis).
+
+        match self.move_data.rev_lookup.find(place) {
+            LookupResult::Parent(_) => None,
+            LookupResult::Exact(mpi) => Some(mpi),
+        }
+    }
+
+    fn check_if_assigned_path_is_moved(
+        &mut self,
+        context: Context,
+        (place, span): (&Place<'tcx>, Span),
+        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
+    ) {
+        // recur down place; dispatch to check_if_path_is_moved when necessary
+        let mut place = place;
+        loop {
+            match *place {
+                Place::Local(_) | Place::Static(_) => {
+                    // assigning to `x` does not require `x` be initialized.
+                    break;
+                }
+                Place::Projection(ref proj) => {
+                    let Projection { ref base, ref elem } = **proj;
+                    match *elem {
+                        ProjectionElem::Deref |
+                        // assigning to *P requires `P` initialized.
+                        ProjectionElem::Index(_/*operand*/) |
+                        ProjectionElem::ConstantIndex { .. } |
+                        // assigning to P[i] requires `P` initialized.
+                        ProjectionElem::Downcast(_/*adt_def*/, _/*variant_idx*/) =>
+                        // assigning to (P->variant) is okay if assigning to `P` is okay
+                        //
+                        // FIXME: is this true even if P is a adt with a dtor?
+                        { }
+
+                        ProjectionElem::Subslice { .. } => {
+                            panic!("we dont allow assignments to subslices, context: {:?}",
+                                   context);
+                        }
+
+                        ProjectionElem::Field(..) => {
+                            // if type of `P` has a dtor, then
+                            // assigning to `P.f` requires `P` itself
+                            // be already initialized
+                            let tcx = self.tcx;
+                            match base.ty(self.mir, tcx).to_ty(tcx).sty {
+                                ty::TyAdt(def, _) if def.has_dtor(tcx) => {
+
+                                    // FIXME: analogous code in
+                                    // check_loans.rs first maps
+                                    // `base` to its base_path.
+
+                                    self.check_if_path_is_moved(
+                                        context, InitializationRequiringAction::Assignment,
+                                        (base, span), flow_state);
+
+                                    // (base initialized; no need to
+                                    // recur further)
+                                    break;
+                                }
+                                _ => {}
+                            }
+                        }
+                    }
+
+                    place = base;
+                    continue;
+                }
+            }
+        }
+    }
+
+    /// Check the permissions for the given place and read or write kind
+    ///
+    /// Returns true if an error is reported, false otherwise.
+    fn check_access_permissions(
+        &self,
+        (place, span): (&Place<'tcx>, Span),
+        kind: ReadOrWrite,
+        is_local_mutation_allowed: LocalMutationIsAllowed,
+    ) -> bool {
+        debug!(
+            "check_access_permissions({:?}, {:?}, {:?})",
+            place,
+            kind,
+            is_local_mutation_allowed
+        );
+        let mut error_reported = false;
+        match kind {
+            Write(WriteKind::MutableBorrow(BorrowKind::Unique)) => {
+                if let Err(_place_err) = self.is_unique(place) {
+                    span_bug!(span, "&unique borrow for {:?} should not fail", place);
+                }
+            }
+            Write(WriteKind::MutableBorrow(BorrowKind::Mut)) => if let Err(place_err) =
+                self.is_mutable(place, is_local_mutation_allowed)
+            {
+                error_reported = true;
+
+                let item_msg = match self.describe_place(place) {
+                    Some(name) => format!("immutable item `{}`", name),
+                    None => "immutable item".to_owned(),
+                };
+
+                let mut err = self.tcx
+                    .cannot_borrow_path_as_mutable(span, &item_msg, Origin::Mir);
+                err.span_label(span, "cannot borrow as mutable");
+
+                if place != place_err {
+                    if let Some(name) = self.describe_place(place_err) {
+                        err.note(&format!("Value not mutable causing this error: `{}`", name));
+                    }
+                }
+
+                err.emit();
+            },
+            Write(WriteKind::Mutate) => {
+                if let Err(place_err) = self.is_mutable(place, is_local_mutation_allowed) {
+                    error_reported = true;
+
+                    let item_msg = match self.describe_place(place) {
+                        Some(name) => format!("immutable item `{}`", name),
+                        None => "immutable item".to_owned(),
+                    };
+
+                    let mut err = self.tcx.cannot_assign(span, &item_msg, Origin::Mir);
+                    err.span_label(span, "cannot mutate");
+
+                    if place != place_err {
+                        if let Some(name) = self.describe_place(place_err) {
+                            err.note(&format!("Value not mutable causing this error: `{}`", name));
+                        }
+                    }
+
+                    err.emit();
+                }
+            }
+            Write(WriteKind::Move) |
+            Write(WriteKind::StorageDeadOrDrop) |
+            Write(WriteKind::MutableBorrow(BorrowKind::Shared)) => {
+                if let Err(_place_err) = self.is_mutable(place, is_local_mutation_allowed) {
+                    self.tcx.sess.delay_span_bug(
+                        span,
+                        &format!(
+                            "Accessing `{:?}` with the kind `{:?}` shouldn't be possible",
+                            place,
+                            kind
+                        ),
+                    );
+                }
+            }
+            Read(ReadKind::Borrow(BorrowKind::Unique)) |
+            Read(ReadKind::Borrow(BorrowKind::Mut)) |
+            Read(ReadKind::Borrow(BorrowKind::Shared)) |
+            Read(ReadKind::Copy) => {} // Access authorized
+        }
+
+        error_reported
+    }
+
+    /// Can this value be written or borrowed mutably
+    fn is_mutable<'d>(
+        &self,
+        place: &'d Place<'tcx>,
+        is_local_mutation_allowed: LocalMutationIsAllowed,
+    ) -> Result<(), &'d Place<'tcx>> {
+        match *place {
+            Place::Local(local) => {
+                let local = &self.mir.local_decls[local];
+                match local.mutability {
+                    Mutability::Not => match is_local_mutation_allowed {
+                        LocalMutationIsAllowed::Yes => Ok(()),
+                        LocalMutationIsAllowed::No => Err(place),
+                    },
+                    Mutability::Mut => Ok(()),
+                }
+            }
+            Place::Static(ref static_) => if !self.tcx.is_static_mut(static_.def_id) {
+                Err(place)
+            } else {
+                Ok(())
+            },
+            Place::Projection(ref proj) => {
+                match proj.elem {
+                    ProjectionElem::Deref => {
+                        let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+
+                        // Check the kind of deref to decide
+                        match base_ty.sty {
+                            ty::TyRef(_, tnm) => {
+                                match tnm.mutbl {
+                                    // Shared borrowed data is never mutable
+                                    hir::MutImmutable => Err(place),
+                                    // Mutably borrowed data is mutable, but only if we have a
+                                    // unique path to the `&mut`
+                                    hir::MutMutable => {
+                                        if self.is_upvar_field_projection(&proj.base).is_some() {
+                                            self.is_mutable(&proj.base, is_local_mutation_allowed)
+                                        } else {
+                                            self.is_unique(&proj.base)
+                                        }
+                                    }
+                                }
+                            }
+                            ty::TyRawPtr(tnm) => {
+                                match tnm.mutbl {
+                                    // `*const` raw pointers are not mutable
+                                    hir::MutImmutable => Err(place),
+                                    // `*mut` raw pointers are always mutable, regardless of context
+                                    // The users have to check by themselve.
+                                    hir::MutMutable => Ok(()),
+                                }
+                            }
+                            // `Box<T>` owns its content, so mutable if its location is mutable
+                            _ if base_ty.is_box() => {
+                                self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
+                            }
+                            // Deref should only be for reference, pointers or boxes
+                            _ => bug!("Deref of unexpected type: {:?}", base_ty),
+                        }
+                    }
+                    // All other projections are owned by their base path, so mutable if
+                    // base path is mutable
+                    ProjectionElem::Field(..) |
+                    ProjectionElem::Index(..) |
+                    ProjectionElem::ConstantIndex { .. } |
+                    ProjectionElem::Subslice { .. } |
+                    ProjectionElem::Downcast(..) => {
+                        let field_projection = self.is_upvar_field_projection(place);
+
+                        if let Some(field) = field_projection {
+                            let decl = &self.mir.upvar_decls[field.index()];
+
+                            return match decl.mutability {
+                                Mutability::Mut => self.is_unique(&proj.base),
+                                Mutability::Not => Err(place),
+                            };
+                        }
+
+                        self.is_mutable(&proj.base, LocalMutationIsAllowed::No)
+                    }
+                }
+            }
+        }
+    }
+
+    /// Does this place have a unique path
+    fn is_unique<'d>(&self, place: &'d Place<'tcx>) -> Result<(), &'d Place<'tcx>> {
+        match *place {
+            Place::Local(..) => {
+                // Local variables are unique
+                Ok(())
+            }
+            Place::Static(..) => {
+                // Static variables are not
+                Err(place)
+            }
+            Place::Projection(ref proj) => {
+                match proj.elem {
+                    ProjectionElem::Deref => {
+                        let base_ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+
+                        // `Box<T>` referent is unique if box is a unique spot
+                        if base_ty.is_box() {
+                            return self.is_unique(&proj.base);
+                        }
+
+                        // Otherwise we check the kind of deref to decide
+                        match base_ty.sty {
+                            ty::TyRef(_, tnm) => {
+                                match tnm.mutbl {
+                                    // place represent an aliased location
+                                    hir::MutImmutable => Err(place),
+                                    // `&mut T` is as unique as the context in which it is found
+                                    hir::MutMutable => self.is_unique(&proj.base),
+                                }
+                            }
+                            ty::TyRawPtr(tnm) => {
+                                match tnm.mutbl {
+                                    // `*mut` can be aliased, but we leave it to user
+                                    hir::MutMutable => Ok(()),
+                                    // `*const` is treated the same as `*mut`
+                                    hir::MutImmutable => Ok(()),
+                                }
+                            }
+                            // Deref should only be for reference, pointers or boxes
+                            _ => bug!("Deref of unexpected type: {:?}", base_ty),
+                        }
+                    }
+                    // Other projections are unique if the base is unique
+                    ProjectionElem::Field(..) |
+                    ProjectionElem::Index(..) |
+                    ProjectionElem::ConstantIndex { .. } |
+                    ProjectionElem::Subslice { .. } |
+                    ProjectionElem::Downcast(..) => self.is_unique(&proj.base),
+                }
+            }
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum NoMovePathFound {
+    ReachedStatic,
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    fn each_borrow_involving_path<F>(
+        &mut self,
+        _context: Context,
+        access_place: (ShallowOrDeep, &Place<'tcx>),
+        flow_state: &InProgress<'cx, 'gcx, 'tcx>,
+        mut op: F,
+    ) where
+        F: FnMut(&mut Self, BorrowIndex, &BorrowData<'tcx>, &Place<'tcx>) -> Control,
+    {
+        let (access, place) = access_place;
+
+        // FIXME: analogous code in check_loans first maps `place` to
+        // its base_path.
+
+        let domain = flow_state.borrows.base_results.operator();
+        let data = domain.borrows();
+
+        // check for loan restricting path P being used. Accounts for
+        // borrows of P, P.a.b, etc.
+        'next_borrow: for i in flow_state.borrows.elems_incoming() {
+            let borrowed = &data[i];
+
+            // Is `place` (or a prefix of it) already borrowed? If
+            // so, that's relevant.
+            //
+            // FIXME: Differs from AST-borrowck; includes drive-by fix
+            // to #38899. Will probably need back-compat mode flag.
+            for accessed_prefix in self.prefixes(place, PrefixSet::All) {
+                if *accessed_prefix == borrowed.place {
+                    // FIXME: pass in enum describing case we are in?
+                    let ctrl = op(self, i, borrowed, accessed_prefix);
+                    if ctrl == Control::Break {
+                        return;
+                    }
+                }
+            }
+
+            // Is `place` a prefix (modulo access type) of the
+            // `borrowed.place`? If so, that's relevant.
+
+            let prefix_kind = match access {
+                Shallow(Some(ArtificialField::Discriminant)) |
+                Shallow(Some(ArtificialField::ArrayLength)) => {
+                    // The discriminant and array length are like
+                    // additional fields on the type; they do not
+                    // overlap any existing data there. Furthermore,
+                    // they cannot actually be a prefix of any
+                    // borrowed place (at least in MIR as it is
+                    // currently.)
+                    continue 'next_borrow;
+                }
+                Shallow(None) => PrefixSet::Shallow,
+                Deep => PrefixSet::Supporting,
+            };
+
+            for borrowed_prefix in self.prefixes(&borrowed.place, prefix_kind) {
+                if borrowed_prefix == place {
+                    // FIXME: pass in enum describing case we are in?
+                    let ctrl = op(self, i, borrowed, borrowed_prefix);
+                    if ctrl == Control::Break {
+                        return;
+                    }
+                }
+            }
+        }
+    }
+}
+
+use self::prefixes::PrefixSet;
+
+/// From the NLL RFC: "The deep [aka 'supporting'] prefixes for an
+/// place are formed by stripping away fields and derefs, except that
+/// we stop when we reach the deref of a shared reference. [...] "
+///
+/// "Shallow prefixes are found by stripping away fields, but stop at
+/// any dereference. So: writing a path like `a` is illegal if `a.b`
+/// is borrowed. But: writing `a` is legal if `*a` is borrowed,
+/// whether or not `a` is a shared or mutable reference. [...] "
+mod prefixes {
+    use super::MirBorrowckCtxt;
+
+    use rustc::hir;
+    use rustc::ty::{self, TyCtxt};
+    use rustc::mir::{Mir, Place, ProjectionElem};
+
+    pub trait IsPrefixOf<'tcx> {
+        fn is_prefix_of(&self, other: &Place<'tcx>) -> bool;
+    }
+
+    impl<'tcx> IsPrefixOf<'tcx> for Place<'tcx> {
+        fn is_prefix_of(&self, other: &Place<'tcx>) -> bool {
+            let mut cursor = other;
+            loop {
+                if self == cursor {
+                    return true;
+                }
+
+                match *cursor {
+                    Place::Local(_) | Place::Static(_) => return false,
+                    Place::Projection(ref proj) => {
+                        cursor = &proj.base;
+                    }
+                }
+            }
+        }
+    }
+
+
+    pub(super) struct Prefixes<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
+        mir: &'cx Mir<'tcx>,
+        tcx: TyCtxt<'cx, 'gcx, 'tcx>,
+        kind: PrefixSet,
+        next: Option<&'cx Place<'tcx>>,
+    }
+
+    #[derive(Copy, Clone, PartialEq, Eq, Debug)]
+    pub(super) enum PrefixSet {
+        /// Doesn't stop until it returns the base case (a Local or
+        /// Static prefix).
+        All,
+        /// Stops at any dereference.
+        Shallow,
+        /// Stops at the deref of a shared reference.
+        Supporting,
+    }
+
+    impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+        /// Returns an iterator over the prefixes of `place`
+        /// (inclusive) from longest to smallest, potentially
+        /// terminating the iteration early based on `kind`.
+        pub(super) fn prefixes(
+            &self,
+            place: &'cx Place<'tcx>,
+            kind: PrefixSet,
+        ) -> Prefixes<'cx, 'gcx, 'tcx> {
+            Prefixes {
+                next: Some(place),
+                kind,
+                mir: self.mir,
+                tcx: self.tcx,
+            }
+        }
+    }
+
+    impl<'cx, 'gcx, 'tcx> Iterator for Prefixes<'cx, 'gcx, 'tcx> {
+        type Item = &'cx Place<'tcx>;
+        fn next(&mut self) -> Option<Self::Item> {
+            let mut cursor = match self.next {
+                None => return None,
+                Some(place) => place,
+            };
+
+            // Post-processing `place`: Enqueue any remaining
+            // work. Also, `place` may not be a prefix itself, but
+            // may hold one further down (e.g. we never return
+            // downcasts here, but may return a base of a downcast).
+
+            'cursor: loop {
+                let proj = match *cursor {
+                    Place::Local(_) | // search yielded this leaf
+                    Place::Static(_) => {
+                        self.next = None;
+                        return Some(cursor);
+                    }
+
+                    Place::Projection(ref proj) => proj,
+                };
+
+                match proj.elem {
+                    ProjectionElem::Field(_ /*field*/, _ /*ty*/) => {
+                        // FIXME: add union handling
+                        self.next = Some(&proj.base);
+                        return Some(cursor);
+                    }
+                    ProjectionElem::Downcast(..) |
+                    ProjectionElem::Subslice { .. } |
+                    ProjectionElem::ConstantIndex { .. } |
+                    ProjectionElem::Index(_) => {
+                        cursor = &proj.base;
+                        continue 'cursor;
+                    }
+                    ProjectionElem::Deref => {
+                        // (handled below)
+                    }
+                }
+
+                assert_eq!(proj.elem, ProjectionElem::Deref);
+
+                match self.kind {
+                    PrefixSet::Shallow => {
+                        // shallow prefixes are found by stripping away
+                        // fields, but stop at *any* dereference.
+                        // So we can just stop the traversal now.
+                        self.next = None;
+                        return Some(cursor);
+                    }
+                    PrefixSet::All => {
+                        // all prefixes: just blindly enqueue the base
+                        // of the projection
+                        self.next = Some(&proj.base);
+                        return Some(cursor);
+                    }
+                    PrefixSet::Supporting => {
+                        // fall through!
+                    }
+                }
+
+                assert_eq!(self.kind, PrefixSet::Supporting);
+                // supporting prefixes: strip away fields and
+                // derefs, except we stop at the deref of a shared
+                // reference.
+
+                let ty = proj.base.ty(self.mir, self.tcx).to_ty(self.tcx);
+                match ty.sty {
+                    ty::TyRawPtr(_) |
+                    ty::TyRef(
+                        _, /*rgn*/
+                        ty::TypeAndMut {
+                            ty: _,
+                            mutbl: hir::MutImmutable,
+                        },
+                    ) => {
+                        // don't continue traversing over derefs of raw pointers or shared borrows.
+                        self.next = None;
+                        return Some(cursor);
+                    }
+
+                    ty::TyRef(
+                        _, /*rgn*/
+                        ty::TypeAndMut {
+                            ty: _,
+                            mutbl: hir::MutMutable,
+                        },
+                    ) => {
+                        self.next = Some(&proj.base);
+                        return Some(cursor);
+                    }
+
+                    ty::TyAdt(..) if ty.is_box() => {
+                        self.next = Some(&proj.base);
+                        return Some(cursor);
+                    }
+
+                    _ => panic!("unknown type fed to Projection Deref."),
+                }
+            }
+        }
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    fn report_use_of_moved_or_uninitialized(
+        &mut self,
+        _context: Context,
+        desired_action: InitializationRequiringAction,
+        (place, span): (&Place<'tcx>, Span),
+        mpi: MovePathIndex,
+        curr_move_out: &IdxSetBuf<MoveOutIndex>,
+    ) {
+        let mois = self.move_data.path_map[mpi]
+            .iter()
+            .filter(|moi| curr_move_out.contains(moi))
+            .collect::<Vec<_>>();
+
+        if mois.is_empty() {
+            let item_msg = match self.describe_place(place) {
+                Some(name) => format!("`{}`", name),
+                None => "value".to_owned(),
+            };
+            self.tcx
+                .cannot_act_on_uninitialized_variable(
+                    span,
+                    desired_action.as_noun(),
+                    &self.describe_place(place).unwrap_or("_".to_owned()),
+                    Origin::Mir,
+                )
+                .span_label(span, format!("use of possibly uninitialized {}", item_msg))
+                .emit();
+        } else {
+            let msg = ""; //FIXME: add "partially " or "collaterally "
+
+            let mut err = self.tcx.cannot_act_on_moved_value(
+                span,
+                desired_action.as_noun(),
+                msg,
+                &self.describe_place(place).unwrap_or("_".to_owned()),
+                Origin::Mir,
+            );
+
+            err.span_label(
+                span,
+                format!(
+                    "value {} here after move",
+                    desired_action.as_verb_in_past_tense()
+                ),
+            );
+            for moi in mois {
+                let move_msg = ""; //FIXME: add " (into closure)"
+                let move_span = self.mir.source_info(self.move_data.moves[*moi].source).span;
+                if span == move_span {
+                    err.span_label(
+                        span,
+                        format!("value moved{} here in previous iteration of loop", move_msg),
+                    );
+                } else {
+                    err.span_label(move_span, format!("value moved{} here", move_msg));
+                };
+            }
+            //FIXME: add note for closure
+            err.emit();
+        }
+    }
+
+    fn report_move_out_while_borrowed(
+        &mut self,
+        _context: Context,
+        (place, span): (&Place<'tcx>, Span),
+        borrow: &BorrowData<'tcx>,
+    ) {
+        let value_msg = match self.describe_place(place) {
+            Some(name) => format!("`{}`", name),
+            None => "value".to_owned(),
+        };
+        let borrow_msg = match self.describe_place(&borrow.place) {
+            Some(name) => format!("`{}`", name),
+            None => "value".to_owned(),
+        };
+        self.tcx
+            .cannot_move_when_borrowed(
+                span,
+                &self.describe_place(place).unwrap_or("_".to_owned()),
+                Origin::Mir,
+            )
+            .span_label(
+                self.retrieve_borrow_span(borrow),
+                format!("borrow of {} occurs here", borrow_msg),
+            )
+            .span_label(span, format!("move out of {} occurs here", value_msg))
+            .emit();
+    }
+
+    fn report_use_while_mutably_borrowed(
+        &mut self,
+        _context: Context,
+        (place, span): (&Place<'tcx>, Span),
+        borrow: &BorrowData<'tcx>,
+    ) {
+        let mut err = self.tcx.cannot_use_when_mutably_borrowed(
+            span,
+            &self.describe_place(place).unwrap_or("_".to_owned()),
+            self.retrieve_borrow_span(borrow),
+            &self.describe_place(&borrow.place).unwrap_or("_".to_owned()),
+            Origin::Mir,
+        );
+
+        err.emit();
+    }
+
+    /// Finds the span of arguments of a closure (within `maybe_closure_span`) and its usage of
+    /// the local assigned at `location`.
+    /// This is done by searching in statements succeeding `location`
+    /// and originating from `maybe_closure_span`.
+    fn find_closure_span(
+        &self,
+        maybe_closure_span: Span,
+        location: Location,
+    ) -> Option<(Span, Span)> {
+        use rustc::hir::ExprClosure;
+        use rustc::mir::AggregateKind;
+
+        let local = if let StatementKind::Assign(Place::Local(local), _) =
+            self.mir[location.block].statements[location.statement_index].kind
+        {
+            local
+        } else {
+            return None;
+        };
+
+        for stmt in &self.mir[location.block].statements[location.statement_index + 1..] {
+            if maybe_closure_span != stmt.source_info.span {
+                break;
+            }
+
+            if let StatementKind::Assign(_, Rvalue::Aggregate(ref kind, ref places)) = stmt.kind {
+                if let AggregateKind::Closure(def_id, _) = **kind {
+                    debug!("find_closure_span: found closure {:?}", places);
+
+                    return if let Some(node_id) = self.tcx.hir.as_local_node_id(def_id) {
+                        let args_span = if let ExprClosure(_, _, _, span, _) =
+                            self.tcx.hir.expect_expr(node_id).node
+                        {
+                            span
+                        } else {
+                            return None;
+                        };
+
+                        self.tcx
+                            .with_freevars(node_id, |freevars| {
+                                for (v, place) in freevars.iter().zip(places) {
+                                    match *place {
+                                        Operand::Copy(Place::Local(l)) |
+                                        Operand::Move(Place::Local(l)) if local == l =>
+                                        {
+                                            debug!(
+                                                "find_closure_span: found captured local {:?}",
+                                                l
+                                            );
+                                            return Some(v.span);
+                                        }
+                                        _ => {}
+                                    }
+                                }
+                                None
+                            })
+                            .map(|var_span| (args_span, var_span))
+                    } else {
+                        None
+                    };
+                }
+            }
+        }
+
+        None
+    }
+
+    fn report_conflicting_borrow(
+        &mut self,
+        context: Context,
+        common_prefix: &Place<'tcx>,
+        (place, span): (&Place<'tcx>, Span),
+        gen_borrow_kind: BorrowKind,
+        issued_borrow: &BorrowData,
+        end_issued_loan_span: Option<Span>,
+    ) {
+        use self::prefixes::IsPrefixOf;
+
+        assert!(common_prefix.is_prefix_of(place));
+        assert!(common_prefix.is_prefix_of(&issued_borrow.place));
+
+        let issued_span = self.retrieve_borrow_span(issued_borrow);
+
+        let new_closure_span = self.find_closure_span(span, context.loc);
+        let span = new_closure_span.map(|(args, _)| args).unwrap_or(span);
+        let old_closure_span = self.find_closure_span(issued_span, issued_borrow.location);
+        let issued_span = old_closure_span
+            .map(|(args, _)| args)
+            .unwrap_or(issued_span);
+
+        let desc_place = self.describe_place(place).unwrap_or("_".to_owned());
+
+        // FIXME: supply non-"" `opt_via` when appropriate
+        let mut err = match (
+            gen_borrow_kind,
+            "immutable",
+            "mutable",
+            issued_borrow.kind,
+            "immutable",
+            "mutable",
+        ) {
+            (BorrowKind::Shared, lft, _, BorrowKind::Mut, _, rgt) |
+            (BorrowKind::Mut, _, lft, BorrowKind::Shared, rgt, _) => self.tcx
+                .cannot_reborrow_already_borrowed(
+                    span,
+                    &desc_place,
+                    "",
+                    lft,
+                    issued_span,
+                    "it",
+                    rgt,
+                    "",
+                    end_issued_loan_span,
+                    Origin::Mir,
+                ),
+
+            (BorrowKind::Mut, _, _, BorrowKind::Mut, _, _) => self.tcx
+                .cannot_mutably_borrow_multiply(
+                    span,
+                    &desc_place,
+                    "",
+                    issued_span,
+                    "",
+                    end_issued_loan_span,
+                    Origin::Mir,
+                ),
+
+            (BorrowKind::Unique, _, _, BorrowKind::Unique, _, _) => self.tcx
+                .cannot_uniquely_borrow_by_two_closures(
+                    span,
+                    &desc_place,
+                    issued_span,
+                    end_issued_loan_span,
+                    Origin::Mir,
+                ),
+
+            (BorrowKind::Unique, _, _, _, _, _) => self.tcx.cannot_uniquely_borrow_by_one_closure(
+                span,
+                &desc_place,
+                "",
+                issued_span,
+                "it",
+                "",
+                end_issued_loan_span,
+                Origin::Mir,
+            ),
+
+            (_, _, _, BorrowKind::Unique, _, _) => self.tcx
+                .cannot_reborrow_already_uniquely_borrowed(
+                    span,
+                    &desc_place,
+                    "it",
+                    "",
+                    issued_span,
+                    "",
+                    end_issued_loan_span,
+                    Origin::Mir,
+                ),
+
+            (BorrowKind::Shared, _, _, BorrowKind::Shared, _, _) => unreachable!(),
+        };
+
+        if let Some((_, var_span)) = old_closure_span {
+            err.span_label(
+                var_span,
+                format!(
+                    "previous borrow occurs due to use of `{}` in closure",
+                    desc_place
+                ),
+            );
+        }
+
+        if let Some((_, var_span)) = new_closure_span {
+            err.span_label(
+                var_span,
+                format!("borrow occurs due to use of `{}` in closure", desc_place),
+            );
+        }
+
+        err.emit();
+    }
+
+    fn report_borrowed_value_does_not_live_long_enough(
+        &mut self,
+        _: Context,
+        (place, span): (&Place<'tcx>, Span),
+        end_span: Option<Span>,
+    ) {
+        let root_place = self.prefixes(place, PrefixSet::All).last().unwrap();
+        let proper_span = match *root_place {
+            Place::Local(local) => self.mir.local_decls[local].source_info.span,
+            _ => span,
+        };
+        let mut err = self.tcx
+            .path_does_not_live_long_enough(span, "borrowed value", Origin::Mir);
+        err.span_label(proper_span, "temporary value created here");
+        err.span_label(span, "temporary value dropped here while still borrowed");
+        err.note("consider using a `let` binding to increase its lifetime");
+
+        if let Some(end) = end_span {
+            err.span_label(end, "temporary value needs to live until here");
+        }
+
+        err.emit();
+    }
+
+    fn report_illegal_mutation_of_borrowed(
+        &mut self,
+        _: Context,
+        (place, span): (&Place<'tcx>, Span),
+        loan: &BorrowData,
+    ) {
+        let mut err = self.tcx.cannot_assign_to_borrowed(
+            span,
+            self.retrieve_borrow_span(loan),
+            &self.describe_place(place).unwrap_or("_".to_owned()),
+            Origin::Mir,
+        );
+
+        err.emit();
+    }
+
+    fn report_illegal_reassignment(
+        &mut self,
+        _context: Context,
+        (place, span): (&Place<'tcx>, Span),
+        assigned_span: Span,
+    ) {
+        let mut err = self.tcx.cannot_reassign_immutable(
+            span,
+            &self.describe_place(place).unwrap_or("_".to_owned()),
+            Origin::Mir,
+        );
+        err.span_label(span, "cannot assign twice to immutable variable");
+        if span != assigned_span {
+            let value_msg = match self.describe_place(place) {
+                Some(name) => format!("`{}`", name),
+                None => "value".to_owned(),
+            };
+            err.span_label(assigned_span, format!("first assignment to {}", value_msg));
+        }
+        err.emit();
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    // End-user visible description of `place` if one can be found. If the
+    // place is a temporary for instance, None will be returned.
+    fn describe_place(&self, place: &Place<'tcx>) -> Option<String> {
+        let mut buf = String::new();
+        match self.append_place_to_string(place, &mut buf, false) {
+            Ok(()) => Some(buf),
+            Err(()) => None,
+        }
+    }
+
+    /// If this is a field projection, and the field is being projected from a closure type,
+    /// then returns the index of the field being projected. Note that this closure will always
+    /// be `self` in the current MIR, because that is the only time we directly access the fields
+    /// of a closure type.
+    fn is_upvar_field_projection(&self, place: &Place<'tcx>) -> Option<Field> {
+        match *place {
+            Place::Projection(ref proj) => match proj.elem {
+                ProjectionElem::Field(field, _ty) => {
+                    let is_projection_from_ty_closure = proj.base
+                        .ty(self.mir, self.tcx)
+                        .to_ty(self.tcx)
+                        .is_closure();
+
+                    if is_projection_from_ty_closure {
+                        Some(field)
+                    } else {
+                        None
+                    }
+                }
+                _ => None,
+            },
+            _ => None,
+        }
+    }
+
+    // Appends end-user visible description of `place` to `buf`.
+    fn append_place_to_string(
+        &self,
+        place: &Place<'tcx>,
+        buf: &mut String,
+        mut autoderef: bool,
+    ) -> Result<(), ()> {
+        match *place {
+            Place::Local(local) => {
+                self.append_local_to_string(local, buf)?;
+            }
+            Place::Static(ref static_) => {
+                buf.push_str(&format!("{}", &self.tcx.item_name(static_.def_id)));
+            }
+            Place::Projection(ref proj) => {
+                match proj.elem {
+                    ProjectionElem::Deref => {
+                        if let Some(field) = self.is_upvar_field_projection(&proj.base) {
+                            let var_index = field.index();
+                            let name = self.mir.upvar_decls[var_index].debug_name.to_string();
+                            if self.mir.upvar_decls[var_index].by_ref {
+                                buf.push_str(&name);
+                            } else {
+                                buf.push_str(&format!("*{}", &name));
+                            }
+                        } else {
+                            if autoderef {
+                                self.append_place_to_string(&proj.base, buf, autoderef)?;
+                            } else {
+                                buf.push_str(&"*");
+                                self.append_place_to_string(&proj.base, buf, autoderef)?;
+                            }
+                        }
+                    }
+                    ProjectionElem::Downcast(..) => {
+                        self.append_place_to_string(&proj.base, buf, autoderef)?;
+                    }
+                    ProjectionElem::Field(field, _ty) => {
+                        autoderef = true;
+
+                        if let Some(field) = self.is_upvar_field_projection(place) {
+                            let var_index = field.index();
+                            let name = self.mir.upvar_decls[var_index].debug_name.to_string();
+                            buf.push_str(&name);
+                        } else {
+                            let field_name = self.describe_field(&proj.base, field);
+                            self.append_place_to_string(&proj.base, buf, autoderef)?;
+                            buf.push_str(&format!(".{}", field_name));
+                        }
+                    }
+                    ProjectionElem::Index(index) => {
+                        autoderef = true;
+
+                        self.append_place_to_string(&proj.base, buf, autoderef)?;
+                        buf.push_str("[");
+                        if let Err(_) = self.append_local_to_string(index, buf) {
+                            buf.push_str("..");
+                        }
+                        buf.push_str("]");
+                    }
+                    ProjectionElem::ConstantIndex { .. } | ProjectionElem::Subslice { .. } => {
+                        autoderef = true;
+                        // Since it isn't possible to borrow an element on a particular index and
+                        // then use another while the borrow is held, don't output indices details
+                        // to avoid confusing the end-user
+                        self.append_place_to_string(&proj.base, buf, autoderef)?;
+                        buf.push_str(&"[..]");
+                    }
+                };
+            }
+        }
+
+        Ok(())
+    }
+
+    // Appends end-user visible description of the `local` place to `buf`. If `local` doesn't have
+    // a name, then `Err` is returned
+    fn append_local_to_string(&self, local_index: Local, buf: &mut String) -> Result<(), ()> {
+        let local = &self.mir.local_decls[local_index];
+        match local.name {
+            Some(name) => {
+                buf.push_str(&format!("{}", name));
+                Ok(())
+            }
+            None => Err(()),
+        }
+    }
+
+    // End-user visible description of the `field`nth field of `base`
+    fn describe_field(&self, base: &Place, field: Field) -> String {
+        match *base {
+            Place::Local(local) => {
+                let local = &self.mir.local_decls[local];
+                self.describe_field_from_ty(&local.ty, field)
+            }
+            Place::Static(ref static_) => self.describe_field_from_ty(&static_.ty, field),
+            Place::Projection(ref proj) => match proj.elem {
+                ProjectionElem::Deref => self.describe_field(&proj.base, field),
+                ProjectionElem::Downcast(def, variant_index) => {
+                    format!("{}", def.variants[variant_index].fields[field.index()].name)
+                }
+                ProjectionElem::Field(_, field_type) => {
+                    self.describe_field_from_ty(&field_type, field)
+                }
+                ProjectionElem::Index(..) |
+                ProjectionElem::ConstantIndex { .. } |
+                ProjectionElem::Subslice { .. } => {
+                    format!("{}", self.describe_field(&proj.base, field))
+                }
+            },
+        }
+    }
+
+    // End-user visible description of the `field_index`nth field of `ty`
+    fn describe_field_from_ty(&self, ty: &ty::Ty, field: Field) -> String {
+        if ty.is_box() {
+            // If the type is a box, the field is described from the boxed type
+            self.describe_field_from_ty(&ty.boxed_ty(), field)
+        } else {
+            match ty.sty {
+                ty::TyAdt(def, _) => if def.is_enum() {
+                    format!("{}", field.index())
+                } else {
+                    format!("{}", def.struct_variant().fields[field.index()].name)
+                },
+                ty::TyTuple(_, _) => format!("{}", field.index()),
+                ty::TyRef(_, tnm) | ty::TyRawPtr(tnm) => {
+                    self.describe_field_from_ty(&tnm.ty, field)
+                }
+                ty::TyArray(ty, _) | ty::TySlice(ty) => self.describe_field_from_ty(&ty, field),
+                ty::TyClosure(closure_def_id, _) => {
+                    // Convert the def-id into a node-id. node-ids are only valid for
+                    // the local code in the current crate, so this returns an `Option` in case
+                    // the closure comes from another crate. But in that case we wouldn't
+                    // be borrowck'ing it, so we can just unwrap:
+                    let node_id = self.tcx.hir.as_local_node_id(closure_def_id).unwrap();
+                    let freevar = self.tcx.with_freevars(node_id, |fv| fv[field.index()]);
+
+                    self.tcx.hir.name(freevar.var_id()).to_string()
+                }
+                _ => {
+                    // Might need a revision when the fields in trait RFC is implemented
+                    // (https://github.com/rust-lang/rfcs/pull/1546)
+                    bug!(
+                        "End-user description not implemented for field access on `{:?}`",
+                        ty.sty
+                    );
+                }
+            }
+        }
+    }
+
+    // Retrieve span of given borrow from the current MIR representation
+    fn retrieve_borrow_span(&self, borrow: &BorrowData) -> Span {
+        self.mir.source_info(borrow.location).span
+    }
+}
+
+impl<'cx, 'gcx, 'tcx> MirBorrowckCtxt<'cx, 'gcx, 'tcx> {
+    // FIXME (#16118): function intended to allow the borrow checker
+    // to be less precise in its handling of Box while still allowing
+    // moves out of a Box. They should be removed when/if we stop
+    // treating Box specially (e.g. when/if DerefMove is added...)
+
+    fn base_path<'d>(&self, place: &'d Place<'tcx>) -> &'d Place<'tcx> {
+        //! Returns the base of the leftmost (deepest) dereference of an
+        //! Box in `place`. If there is no dereference of an Box
+        //! in `place`, then it just returns `place` itself.
+
+        let mut cursor = place;
+        let mut deepest = place;
+        loop {
+            let proj = match *cursor {
+                Place::Local(..) | Place::Static(..) => return deepest,
+                Place::Projection(ref proj) => proj,
+            };
+            if proj.elem == ProjectionElem::Deref
+                && place.ty(self.mir, self.tcx).to_ty(self.tcx).is_box()
+            {
+                deepest = &proj.base;
+            }
+            cursor = &proj.base;
+        }
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+struct Context {
+    kind: ContextKind,
+    loc: Location,
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, Debug)]
+enum ContextKind {
+    AssignLhs,
+    AssignRhs,
+    SetDiscrim,
+    InlineAsm,
+    SwitchInt,
+    Drop,
+    DropAndReplace,
+    CallOperator,
+    CallOperand,
+    CallDest,
+    Assert,
+    Yield,
+    StorageDead,
+}
+
+impl ContextKind {
+    fn new(self, loc: Location) -> Context {
+        Context {
+            kind: self,
+            loc: loc,
+        }
+    }
+}
+
+impl<'b, 'gcx, 'tcx> InProgress<'b, 'gcx, 'tcx> {
+    fn new(
+        borrows: FlowInProgress<Borrows<'b, 'gcx, 'tcx>>,
+        inits: FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>,
+        uninits: FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>,
+        move_outs: FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>,
+        ever_inits: FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>,
+    ) -> Self {
+        InProgress {
+            borrows,
+            inits,
+            uninits,
+            move_outs,
+            ever_inits,
+        }
+    }
+
+    fn each_flow<XB, XI, XU, XM, XE>(
+        &mut self,
+        mut xform_borrows: XB,
+        mut xform_inits: XI,
+        mut xform_uninits: XU,
+        mut xform_move_outs: XM,
+        mut xform_ever_inits: XE,
+    ) where
+        XB: FnMut(&mut FlowInProgress<Borrows<'b, 'gcx, 'tcx>>),
+        XI: FnMut(&mut FlowInProgress<MaybeInitializedLvals<'b, 'gcx, 'tcx>>),
+        XU: FnMut(&mut FlowInProgress<MaybeUninitializedLvals<'b, 'gcx, 'tcx>>),
+        XM: FnMut(&mut FlowInProgress<MovingOutStatements<'b, 'gcx, 'tcx>>),
+        XE: FnMut(&mut FlowInProgress<EverInitializedLvals<'b, 'gcx, 'tcx>>),
+    {
+        xform_borrows(&mut self.borrows);
+        xform_inits(&mut self.inits);
+        xform_uninits(&mut self.uninits);
+        xform_move_outs(&mut self.move_outs);
+        xform_ever_inits(&mut self.ever_inits);
+    }
+
+    fn summary(&self) -> String {
+        let mut s = String::new();
+
+        s.push_str("borrows in effect: [");
+        let mut saw_one = false;
+        self.borrows.each_state_bit(|borrow| {
+            if saw_one {
+                s.push_str(", ");
+            };
+            saw_one = true;
+            let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
+            s.push_str(&format!("{}", borrow_data));
+        });
+        s.push_str("] ");
+
+        s.push_str("borrows generated: [");
+        let mut saw_one = false;
+        self.borrows.each_gen_bit(|borrow| {
+            if saw_one {
+                s.push_str(", ");
+            };
+            saw_one = true;
+            let borrow_data = &self.borrows.base_results.operator().borrows()[borrow];
+            s.push_str(&format!("{}", borrow_data));
+        });
+        s.push_str("] ");
+
+        s.push_str("inits: [");
+        let mut saw_one = false;
+        self.inits.each_state_bit(|mpi_init| {
+            if saw_one {
+                s.push_str(", ");
+            };
+            saw_one = true;
+            let move_path = &self.inits.base_results.operator().move_data().move_paths[mpi_init];
+            s.push_str(&format!("{}", move_path));
+        });
+        s.push_str("] ");
+
+        s.push_str("uninits: [");
+        let mut saw_one = false;
+        self.uninits.each_state_bit(|mpi_uninit| {
+            if saw_one {
+                s.push_str(", ");
+            };
+            saw_one = true;
+            let move_path =
+                &self.uninits.base_results.operator().move_data().move_paths[mpi_uninit];
+            s.push_str(&format!("{}", move_path));
+        });
+        s.push_str("] ");
+
+        s.push_str("move_out: [");
+        let mut saw_one = false;
+        self.move_outs.each_state_bit(|mpi_move_out| {
+            if saw_one {
+                s.push_str(", ");
+            };
+            saw_one = true;
+            let move_out = &self.move_outs.base_results.operator().move_data().moves[mpi_move_out];
+            s.push_str(&format!("{:?}", move_out));
+        });
+        s.push_str("] ");
+
+        s.push_str("ever_init: [");
+        let mut saw_one = false;
+        self.ever_inits.each_state_bit(|mpi_ever_init| {
+            if saw_one {
+                s.push_str(", ");
+            };
+            saw_one = true;
+            let ever_init =
+                &self.ever_inits.base_results.operator().move_data().inits[mpi_ever_init];
+            s.push_str(&format!("{:?}", ever_init));
+        });
+        s.push_str("]");
+
+        return s;
+    }
+}
+
+impl<'tcx, T> FlowInProgress<T>
+where
+    T: HasMoveData<'tcx> + BitDenotation<Idx = MovePathIndex>,
+{
+    fn has_any_child_of(&self, mpi: T::Idx) -> Option<T::Idx> {
+        let move_data = self.base_results.operator().move_data();
+
+        let mut todo = vec![mpi];
+        let mut push_siblings = false; // don't look at siblings of original `mpi`.
+        while let Some(mpi) = todo.pop() {
+            if self.curr_state.contains(&mpi) {
+                return Some(mpi);
+            }
+            let move_path = &move_data.move_paths[mpi];
+            if let Some(child) = move_path.first_child {
+                todo.push(child);
+            }
+            if push_siblings {
+                if let Some(sibling) = move_path.next_sibling {
+                    todo.push(sibling);
+                }
+            } else {
+                // after we've processed the original `mpi`, we should
+                // always traverse the siblings of any of its
+                // children.
+                push_siblings = true;
+            }
+        }
+        return None;
+    }
+}
+
+impl<BD> FlowInProgress<BD>
+where
+    BD: BitDenotation,
+{
+    fn each_state_bit<F>(&self, f: F)
+    where
+        F: FnMut(BD::Idx),
+    {
+        self.curr_state
+            .each_bit(self.base_results.operator().bits_per_block(), f)
+    }
+
+    fn each_gen_bit<F>(&self, f: F)
+    where
+        F: FnMut(BD::Idx),
+    {
+        self.stmt_gen
+            .each_bit(self.base_results.operator().bits_per_block(), f)
+    }
+
+    fn new(results: DataflowResults<BD>) -> Self {
+        let bits_per_block = results.sets().bits_per_block();
+        let curr_state = IdxSetBuf::new_empty(bits_per_block);
+        let stmt_gen = IdxSetBuf::new_empty(bits_per_block);
+        let stmt_kill = IdxSetBuf::new_empty(bits_per_block);
+        FlowInProgress {
+            base_results: results,
+            curr_state: curr_state,
+            stmt_gen: stmt_gen,
+            stmt_kill: stmt_kill,
+        }
+    }
+
+    fn reset_to_entry_of(&mut self, bb: BasicBlock) {
+        (*self.curr_state).clone_from(self.base_results.sets().on_entry_set_for(bb.index()));
+    }
+
+    fn reconstruct_statement_effect(&mut self, loc: Location) {
+        self.stmt_gen.reset_to_empty();
+        self.stmt_kill.reset_to_empty();
+        let mut ignored = IdxSetBuf::new_empty(0);
+        let mut sets = BlockSets {
+            on_entry: &mut ignored,
+            gen_set: &mut self.stmt_gen,
+            kill_set: &mut self.stmt_kill,
+        };
+        self.base_results
+            .operator()
+            .statement_effect(&mut sets, loc);
+    }
+
+    fn reconstruct_terminator_effect(&mut self, loc: Location) {
+        self.stmt_gen.reset_to_empty();
+        self.stmt_kill.reset_to_empty();
+        let mut ignored = IdxSetBuf::new_empty(0);
+        let mut sets = BlockSets {
+            on_entry: &mut ignored,
+            gen_set: &mut self.stmt_gen,
+            kill_set: &mut self.stmt_kill,
+        };
+        self.base_results
+            .operator()
+            .terminator_effect(&mut sets, loc);
+    }
+
+    fn apply_local_effect(&mut self) {
+        self.curr_state.union(&self.stmt_gen);
+        self.curr_state.subtract(&self.stmt_kill);
+    }
+
+    fn elems_incoming(&self) -> indexed_set::Elems<BD::Idx> {
+        let univ = self.base_results.sets().bits_per_block();
+        self.curr_state.elems(univ)
+    }
+
+    fn with_elems_outgoing<F>(&self, f: F)
+    where
+        F: FnOnce(indexed_set::Elems<BD::Idx>),
+    {
+        let mut curr_state = self.curr_state.clone();
+        curr_state.union(&self.stmt_gen);
+        curr_state.subtract(&self.stmt_kill);
+        let univ = self.base_results.sets().bits_per_block();
+        f(curr_state.elems(univ));
+    }
+}
diff --git a/src/librustc_mir/borrow_check/nll/constraint_generation.rs b/src/librustc_mir/borrow_check/nll/constraint_generation.rs
new file mode 100644 (file)
index 0000000..460d49a
--- /dev/null
@@ -0,0 +1,288 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::hir;
+use rustc::mir::{Local, Location, Place, Mir, Rvalue};
+use rustc::mir::visit::Visitor;
+use rustc::mir::Place::Projection;
+use rustc::mir::{PlaceProjection, ProjectionElem};
+use rustc::infer::InferCtxt;
+use rustc::traits::{self, ObligationCause};
+use rustc::ty::{self, Ty};
+use rustc::ty::fold::TypeFoldable;
+use rustc::util::common::ErrorReported;
+use rustc_data_structures::fx::FxHashSet;
+use syntax::codemap::DUMMY_SP;
+use borrow_check::FlowInProgress;
+use dataflow::MaybeInitializedLvals;
+use dataflow::move_paths::{MoveData, HasMoveData};
+
+use super::LivenessResults;
+use super::ToRegionVid;
+use super::region_infer::RegionInferenceContext;
+
+pub(super) fn generate_constraints<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
+    regioncx: &mut RegionInferenceContext<'tcx>,
+    mir: &Mir<'tcx>,
+    param_env: ty::ParamEnv<'tcx>,
+    liveness: &LivenessResults,
+    flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
+    move_data: &MoveData<'tcx>,
+) {
+    ConstraintGeneration {
+        infcx,
+        regioncx,
+        mir,
+        liveness,
+        param_env,
+        flow_inits,
+        move_data,
+    }.add_constraints();
+}
+
+/// 'cg = the duration of the constraint generation process itself.
+struct ConstraintGeneration<'cg, 'cx: 'cg, 'gcx: 'tcx, 'tcx: 'cx> {
+    infcx: &'cg InferCtxt<'cx, 'gcx, 'tcx>,
+    regioncx: &'cg mut RegionInferenceContext<'tcx>,
+    mir: &'cg Mir<'tcx>,
+    liveness: &'cg LivenessResults,
+    param_env: ty::ParamEnv<'tcx>,
+    flow_inits: &'cg mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
+    move_data: &'cg MoveData<'tcx>,
+}
+
+impl<'cx, 'cg, 'gcx, 'tcx> ConstraintGeneration<'cx, 'cg, 'gcx, 'tcx> {
+    fn add_constraints(&mut self) {
+        self.add_liveness_constraints();
+        self.add_borrow_constraints();
+    }
+
+    /// Liveness constraints:
+    ///
+    /// > If a variable V is live at point P, then all regions R in the type of V
+    /// > must include the point P.
+    fn add_liveness_constraints(&mut self) {
+        debug!("add_liveness_constraints()");
+        for bb in self.mir.basic_blocks().indices() {
+            debug!("add_liveness_constraints: bb={:?}", bb);
+
+            self.liveness
+                .regular
+                .simulate_block(self.mir, bb, |location, live_locals| {
+                    for live_local in live_locals.iter() {
+                        let live_local_ty = self.mir.local_decls[live_local].ty;
+                        self.add_regular_live_constraint(live_local_ty, location);
+                    }
+                });
+
+            let mut all_live_locals: Vec<(Location, Vec<Local>)> = vec![];
+            self.liveness.drop.simulate_block(self.mir, bb, |location, live_locals| {
+                all_live_locals.push((location, live_locals.iter().collect()));
+            });
+            debug!("add_liveness_constraints: all_live_locals={:#?}", all_live_locals);
+
+            let terminator_index = self.mir.basic_blocks()[bb].statements.len();
+            self.flow_inits.reset_to_entry_of(bb);
+            while let Some((location, live_locals)) = all_live_locals.pop() {
+                for live_local in live_locals {
+                    debug!("add_liveness_constraints: location={:?} live_local={:?}", location,
+                           live_local);
+
+                    self.flow_inits.each_state_bit(|mpi_init| {
+                        debug!("add_liveness_constraints: location={:?} initialized={:?}",
+                               location,
+                               &self.flow_inits
+                                   .base_results
+                                   .operator()
+                                   .move_data()
+                                   .move_paths[mpi_init]);
+                    });
+
+                    let mpi = self.move_data.rev_lookup.find_local(live_local);
+                    if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
+                        debug!("add_liveness_constraints: mpi={:?} has initialized child {:?}",
+                               self.move_data.move_paths[mpi],
+                               self.move_data.move_paths[initialized_child]);
+
+                        let live_local_ty = self.mir.local_decls[live_local].ty;
+                        self.add_drop_live_constraint(live_local_ty, location);
+                    }
+                }
+
+                if location.statement_index == terminator_index {
+                    debug!("add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
+                           location);
+                    self.flow_inits.reconstruct_terminator_effect(location);
+                } else {
+                    debug!("add_liveness_constraints: reconstruct_statement_effect from {:#?}",
+                           location);
+                    self.flow_inits.reconstruct_statement_effect(location);
+                }
+                self.flow_inits.apply_local_effect();
+            }
+        }
+    }
+
+    /// Some variable with type `live_ty` is "regular live" at
+    /// `location` -- i.e., it may be used later. This means that all
+    /// regions appearing in the type `live_ty` must be live at
+    /// `location`.
+    fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!(
+            "add_regular_live_constraint(live_ty={:?}, location={:?})",
+            live_ty,
+            location
+        );
+
+        self.infcx
+            .tcx
+            .for_each_free_region(&live_ty, |live_region| {
+                let vid = live_region.to_region_vid();
+                self.regioncx.add_live_point(vid, location);
+            });
+    }
+
+    /// Some variable with type `live_ty` is "drop live" at `location`
+    /// -- i.e., it may be dropped later. This means that *some* of
+    /// the regions in its type must be live at `location`. The
+    /// precise set will depend on the dropck constraints, and in
+    /// particular this takes `#[may_dangle]` into account.
+    fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
+        debug!(
+            "add_drop_live_constraint(dropped_ty={:?}, location={:?})",
+            dropped_ty,
+            location
+        );
+
+        let tcx = self.infcx.tcx;
+        let mut types = vec![(dropped_ty, 0)];
+        let mut known = FxHashSet();
+        while let Some((ty, depth)) = types.pop() {
+            let span = DUMMY_SP; // FIXME
+            let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
+                Ok(result) => result,
+                Err(ErrorReported) => {
+                    continue;
+                }
+            };
+
+            let ty::DtorckConstraint {
+                outlives,
+                dtorck_types,
+            } = result;
+
+            // All things in the `outlives` array may be touched by
+            // the destructor and must be live at this point.
+            for outlive in outlives {
+                if let Some(ty) = outlive.as_type() {
+                    self.add_regular_live_constraint(ty, location);
+                } else if let Some(r) = outlive.as_region() {
+                    self.add_regular_live_constraint(r, location);
+                } else {
+                    bug!()
+                }
+            }
+
+            // However, there may also be some types that
+            // `dtorck_constraint_for_ty` could not resolve (e.g.,
+            // associated types and parameters). We need to normalize
+            // associated types here and possibly recursively process.
+            for ty in dtorck_types {
+                let cause = ObligationCause::dummy();
+                // We know that our original `dropped_ty` is well-formed,
+                // so region obligations resulting from this normalization
+                // should always hold.
+                //
+                // Therefore we ignore them instead of trying to match
+                // them up with a location.
+                let fulfillcx = traits::FulfillmentContext::new_ignoring_regions();
+                match traits::fully_normalize_with_fulfillcx(
+                    self.infcx, fulfillcx, cause, self.param_env, &ty
+                ) {
+                    Ok(ty) => match ty.sty {
+                        ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
+                            self.add_regular_live_constraint(ty, location);
+                        }
+
+                        _ => if known.insert(ty) {
+                            types.push((ty, depth + 1));
+                        },
+                    },
+
+                    Err(errors) => {
+                        self.infcx.report_fulfillment_errors(&errors, None);
+                    }
+                }
+            }
+        }
+    }
+
+    fn add_borrow_constraints(&mut self) {
+        self.visit_mir(self.mir);
+    }
+
+    fn add_reborrow_constraint(
+        &mut self,
+        location: Location,
+        borrow_region: ty::Region<'tcx>,
+        borrowed_place: &Place<'tcx>,
+    ) {
+        if let Projection(ref proj) = *borrowed_place {
+            let PlaceProjection { ref base, ref elem } = **proj;
+
+            if let ProjectionElem::Deref = *elem {
+                let tcx = self.infcx.tcx;
+                let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
+                let base_sty = &base_ty.sty;
+
+                if let ty::TyRef(base_region, ty::TypeAndMut{ ty: _, mutbl }) = *base_sty {
+                    match mutbl {
+                        hir::Mutability::MutImmutable => { },
+
+                        hir::Mutability::MutMutable => {
+                            self.add_reborrow_constraint(location, borrow_region, base);
+                        },
+                    }
+
+                    let span = self.mir.source_info(location).span;
+                    self.regioncx.add_outlives(span,
+                                               base_region.to_region_vid(),
+                                               borrow_region.to_region_vid(),
+                                               location.successor_within_block());
+                }
+            }
+        }
+    }
+}
+
+impl<'cg, 'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cg, 'cx, 'gcx, 'tcx> {
+    fn visit_rvalue(&mut self,
+                    rvalue: &Rvalue<'tcx>,
+                    location: Location) {
+        debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
+
+        // Look for an rvalue like:
+        //
+        //     & L
+        //
+        // where L is the path that is borrowed. In that case, we have
+        // to add the reborrow constraints (which don't fall out
+        // naturally from the type-checker).
+        if let Rvalue::Ref(region, _bk, ref borrowed_place) = *rvalue {
+            self.add_reborrow_constraint(location, region, borrowed_place);
+        }
+
+        self.super_rvalue(rvalue, location);
+    }
+}
diff --git a/src/librustc_mir/borrow_check/nll/free_regions.rs b/src/librustc_mir/borrow_check/nll/free_regions.rs
new file mode 100644 (file)
index 0000000..92a8a71
--- /dev/null
@@ -0,0 +1,90 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//! Code to extract the free regions declared on a function and the
+//! relationships between them. For example:
+//!
+//! ```
+//! fn foo<'a, 'b, 'c: 'b>() { }
+//! ```
+//!
+//! here we would be returning a map assigning each of `{'a, 'b, 'c}`
+//! to an index, as well as the `FreeRegionMap` which can compute
+//! relationships between them.
+//!
+//! The code in this file doesn't *do anything* with those results; it
+//! just returns them for other code to use.
+
+use rustc::hir::def_id::DefId;
+use rustc::infer::InferCtxt;
+use rustc::middle::free_region::FreeRegionMap;
+use rustc::ty::{self, RegionVid};
+use rustc::ty::subst::Substs;
+use rustc::util::nodemap::FxHashMap;
+use rustc_data_structures::indexed_vec::Idx;
+
+#[derive(Debug)]
+pub struct FreeRegions<'tcx> {
+    /// Given a free region defined on this function (either early- or
+    /// late-bound), this maps it to its internal region index. When
+    /// the region context is created, the first N variables will be
+    /// created based on these indices.
+    pub indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
+
+    /// The map from the typeck tables telling us how to relate free regions.
+    pub free_region_map: &'tcx FreeRegionMap<'tcx>,
+}
+
+pub fn free_regions<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    item_def_id: DefId,
+) -> FreeRegions<'tcx> {
+    debug!("free_regions(item_def_id={:?})", item_def_id);
+
+    let mut indices = FxHashMap();
+
+    // `'static` is always free.
+    insert_free_region(&mut indices, infcx.tcx.types.re_static);
+
+    // Extract the early regions.
+    let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
+    for item_subst in item_substs {
+        if let Some(region) = item_subst.as_region() {
+            insert_free_region(&mut indices, region);
+        }
+    }
+
+    // Extract the late-bound regions. Use the liberated fn sigs,
+    // where the late-bound regions will have been converted into free
+    // regions, and add them to the map.
+    let item_id = infcx.tcx.hir.as_local_node_id(item_def_id).unwrap();
+    let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
+    let tables = infcx.tcx.typeck_tables_of(item_def_id);
+    let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
+    infcx
+        .tcx
+        .for_each_free_region(&fn_sig.inputs_and_output, |region| {
+            if let ty::ReFree(_) = *region {
+                insert_free_region(&mut indices, region);
+            }
+        });
+
+    debug!("free_regions: indices={:#?}", indices);
+
+    FreeRegions { indices, free_region_map: &tables.free_region_map }
+}
+
+fn insert_free_region<'tcx>(
+    free_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
+    region: ty::Region<'tcx>,
+) {
+    let next = RegionVid::new(free_regions.len());
+    free_regions.entry(region).or_insert(next);
+}
diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs
new file mode 100644 (file)
index 0000000..213cf52
--- /dev/null
@@ -0,0 +1,231 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::hir::def_id::DefId;
+use rustc::mir::Mir;
+use rustc::infer::InferCtxt;
+use rustc::ty::{self, RegionKind, RegionVid};
+use rustc::util::nodemap::FxHashMap;
+use std::collections::BTreeSet;
+use transform::MirSource;
+use transform::type_check;
+use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
+use borrow_check::FlowInProgress;
+use dataflow::MaybeInitializedLvals;
+use dataflow::move_paths::MoveData;
+
+use util as mir_util;
+use self::mir_util::PassWhere;
+
+mod constraint_generation;
+mod subtype_constraint_generation;
+mod free_regions;
+use self::free_regions::FreeRegions;
+
+pub(crate) mod region_infer;
+use self::region_infer::RegionInferenceContext;
+
+mod renumber;
+
+/// Rewrites the regions in the MIR to use NLL variables, also
+/// scraping out the set of free regions (e.g., region parameters)
+/// declared on the function. That set will need to be given to
+/// `compute_regions`.
+pub(in borrow_check) fn replace_regions_in_mir<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
+    def_id: DefId,
+    mir: &mut Mir<'tcx>,
+) -> FreeRegions<'tcx> {
+    // Compute named region information.
+    let free_regions = free_regions::free_regions(infcx, def_id);
+
+    // Replace all regions with fresh inference variables.
+    renumber::renumber_mir(infcx, &free_regions, mir);
+
+    free_regions
+}
+
+/// Computes the (non-lexical) regions from the input MIR.
+///
+/// This may result in errors being reported.
+pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'cx, 'gcx, 'tcx>,
+    def_id: DefId,
+    free_regions: FreeRegions<'tcx>,
+    mir: &Mir<'tcx>,
+    param_env: ty::ParamEnv<'gcx>,
+    flow_inits: &mut FlowInProgress<MaybeInitializedLvals<'cx, 'gcx, 'tcx>>,
+    move_data: &MoveData<'tcx>,
+) -> RegionInferenceContext<'tcx> {
+    // Run the MIR type-checker.
+    let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
+    let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
+
+    // Create the region inference context, taking ownership of the region inference
+    // data that was contained in `infcx`.
+    let var_origins = infcx.take_region_var_origins();
+    let mut regioncx = RegionInferenceContext::new(var_origins, &free_regions, mir);
+    subtype_constraint_generation::generate(&mut regioncx, &free_regions, mir, constraint_sets);
+
+    // Compute what is live where.
+    let liveness = &LivenessResults {
+        regular: liveness::liveness_of_locals(
+            &mir,
+            LivenessMode {
+                include_regular_use: true,
+                include_drops: false,
+            },
+        ),
+
+        drop: liveness::liveness_of_locals(
+            &mir,
+            LivenessMode {
+                include_regular_use: false,
+                include_drops: true,
+            },
+        ),
+    };
+
+    // Generate non-subtyping constraints.
+    constraint_generation::generate_constraints(
+        infcx,
+        &mut regioncx,
+        &mir,
+        param_env,
+        liveness,
+        flow_inits,
+        move_data,
+    );
+
+    // Solve the region constraints.
+    regioncx.solve(infcx, &mir);
+
+    // Dump MIR results into a file, if that is enabled. This let us
+    // write unit-tests.
+    dump_mir_results(infcx, liveness, MirSource::item(def_id), &mir, &regioncx);
+
+    regioncx
+}
+
+struct LivenessResults {
+    regular: LivenessResult,
+    drop: LivenessResult,
+}
+
+fn dump_mir_results<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    liveness: &LivenessResults,
+    source: MirSource,
+    mir: &Mir<'tcx>,
+    regioncx: &RegionInferenceContext,
+) {
+    if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
+        return;
+    }
+
+    let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
+        .indices()
+        .flat_map(|bb| {
+            let mut results = vec![];
+            liveness
+                .regular
+                .simulate_block(&mir, bb, |location, local_set| {
+                    results.push((location, local_set.clone()));
+                });
+            results
+        })
+        .collect();
+
+    let drop_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
+        .indices()
+        .flat_map(|bb| {
+            let mut results = vec![];
+            liveness
+                .drop
+                .simulate_block(&mir, bb, |location, local_set| {
+                    results.push((location, local_set.clone()));
+                });
+            results
+        })
+        .collect();
+
+    mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| {
+        match pass_where {
+            // Before the CFG, dump out the values for each region variable.
+            PassWhere::BeforeCFG => for region in regioncx.regions() {
+                writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?;
+            },
+
+            // Before each basic block, dump out the values
+            // that are live on entry to the basic block.
+            PassWhere::BeforeBlock(bb) => {
+                let s = live_variable_set(&liveness.regular.ins[bb], &liveness.drop.ins[bb]);
+                writeln!(out, "    | Live variables on entry to {:?}: {}", bb, s)?;
+            }
+
+            PassWhere::InCFG(location) => {
+                let s = live_variable_set(
+                    &regular_liveness_per_location[&location],
+                    &drop_liveness_per_location[&location],
+                );
+                writeln!(out, "            | Live variables at {:?}: {}", location, s)?;
+            }
+
+            PassWhere::AfterCFG => {}
+        }
+        Ok(())
+    });
+}
+
+/// Right now, we piggy back on the `ReVar` to store our NLL inference
+/// regions. These are indexed with `RegionVid`. This method will
+/// assert that the region is a `ReVar` and extract its interal index.
+/// This is reasonable because in our MIR we replace all free regions
+/// with inference variables.
+pub trait ToRegionVid {
+    fn to_region_vid(&self) -> RegionVid;
+}
+
+impl ToRegionVid for RegionKind {
+    fn to_region_vid(&self) -> RegionVid {
+        if let &ty::ReVar(vid) = self {
+            vid
+        } else {
+            bug!("region is not an ReVar: {:?}", self)
+        }
+    }
+}
+
+fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String {
+    // sort and deduplicate:
+    let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect();
+
+    // construct a string with each local, including `(drop)` if it is
+    // only dropped, versus a regular use.
+    let mut string = String::new();
+    for local in all_locals {
+        string.push_str(&format!("{:?}", local));
+
+        if !regular.contains(&local) {
+            assert!(drops.contains(&local));
+            string.push_str(" (drop)");
+        }
+
+        string.push_str(", ");
+    }
+
+    let len = if string.is_empty() {
+        0
+    } else {
+        string.len() - 2
+    };
+
+    format!("[{}]", &string[..len])
+}
diff --git a/src/librustc_mir/borrow_check/nll/region_infer.rs b/src/librustc_mir/borrow_check/nll/region_infer.rs
new file mode 100644 (file)
index 0000000..f60bd3c
--- /dev/null
@@ -0,0 +1,512 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use super::free_regions::FreeRegions;
+use rustc::infer::InferCtxt;
+use rustc::infer::RegionVariableOrigin;
+use rustc::infer::NLLRegionVariableOrigin;
+use rustc::infer::region_constraints::VarOrigins;
+use rustc::middle::free_region::FreeRegionMap;
+use rustc::mir::{Location, Mir};
+use rustc::ty::{self, RegionVid};
+use rustc_data_structures::indexed_vec::IndexVec;
+use rustc_data_structures::fx::FxHashSet;
+use std::collections::BTreeSet;
+use std::fmt;
+use syntax_pos::Span;
+
+pub struct RegionInferenceContext<'tcx> {
+    /// Contains the definition for every region variable.  Region
+    /// variables are identified by their index (`RegionVid`). The
+    /// definition contains information about where the region came
+    /// from as well as its final inferred value.
+    definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
+
+    /// The liveness constraints added to each region. For most
+    /// regions, these start out empty and steadily grow, though for
+    /// each free region R they start out containing the entire CFG
+    /// and `end(R)`.
+    liveness_constraints: IndexVec<RegionVid, Region>,
+
+    /// The final inferred values of the inference variables; `None`
+    /// until `solve` is invoked.
+    inferred_values: Option<IndexVec<RegionVid, Region>>,
+
+    /// The constraints we have accumulated and used during solving.
+    constraints: Vec<Constraint>,
+
+    free_region_map: &'tcx FreeRegionMap<'tcx>,
+}
+
+struct RegionDefinition<'tcx> {
+    /// Why we created this variable. Mostly these will be
+    /// `RegionVariableOrigin::NLL`, but some variables get created
+    /// elsewhere in the code with other causes (e.g., instantiation
+    /// late-bound-regions).
+    origin: RegionVariableOrigin,
+
+    /// If this is a free-region, then this is `Some(X)` where `X` is
+    /// the name of the region.
+    name: Option<ty::Region<'tcx>>,
+}
+
+/// The value of an individual region variable. Region variables
+/// consist of a set of points in the CFG as well as a set of "free
+/// regions", which are sometimes written as `end(R)`. These
+/// correspond to the named lifetimes and refer to portions of the
+/// caller's control-flow graph -- specifically some portion that can
+/// be reached after we return.
+#[derive(Clone, Default, PartialEq, Eq)]
+struct Region {
+    points: BTreeSet<Location>,
+    free_regions: BTreeSet<RegionVid>,
+}
+
+impl fmt::Debug for Region {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        formatter
+            .debug_set()
+            .entries(&self.points)
+            .entries(&self.free_regions)
+            .finish()
+    }
+}
+
+impl Region {
+    fn add_point(&mut self, point: Location) -> bool {
+        self.points.insert(point)
+    }
+
+    fn add_free_region(&mut self, region: RegionVid) -> bool {
+        self.free_regions.insert(region)
+    }
+
+    fn contains_point(&self, point: Location) -> bool {
+        self.points.contains(&point)
+    }
+}
+
+#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
+pub struct Constraint {
+    // NB. The ordering here is not significant for correctness, but
+    // it is for convenience. Before we dump the constraints in the
+    // debugging logs, we sort them, and we'd like the "super region"
+    // to be first, etc. (In particular, span should remain last.)
+    /// The region SUP must outlive SUB...
+    sup: RegionVid,
+
+    /// Region that must be outlived.
+    sub: RegionVid,
+
+    /// At this location.
+    point: Location,
+
+    /// Where did this constraint arise?
+    span: Span,
+}
+
+impl<'tcx> RegionInferenceContext<'tcx> {
+    /// Creates a new region inference context with a total of
+    /// `num_region_variables` valid inference variables; the first N
+    /// of those will be constant regions representing the free
+    /// regions defined in `free_regions`.
+    pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self {
+        let num_region_variables = var_origins.len();
+
+        // Create a RegionDefinition for each inference variable.
+        let definitions = var_origins
+            .into_iter()
+            .map(|origin| RegionDefinition::new(origin))
+            .collect();
+
+        let mut result = Self {
+            definitions: definitions,
+            liveness_constraints: IndexVec::from_elem_n(Region::default(), num_region_variables),
+            inferred_values: None,
+            constraints: Vec::new(),
+            free_region_map: free_regions.free_region_map,
+        };
+
+        result.init_free_regions(free_regions, mir);
+
+        result
+    }
+
+    /// Initializes the region variables for each free region
+    /// (lifetime parameter). The first N variables always correspond
+    /// to the free regions appearing in the function signature (both
+    /// named and anonymous) and where clauses. This function iterates
+    /// over those regions and initializes them with minimum values.
+    ///
+    /// For example:
+    ///
+    ///     fn foo<'a, 'b>(..) where 'a: 'b
+    ///
+    /// would initialize two variables like so:
+    ///
+    ///     R0 = { CFG, R0 } // 'a
+    ///     R1 = { CFG, R0, R1 } // 'b
+    ///
+    /// Here, R0 represents `'a`, and it contains (a) the entire CFG
+    /// and (b) any free regions that it outlives, which in this case
+    /// is just itself. R1 (`'b`) in contrast also outlives `'a` and
+    /// hence contains R0 and R1.
+    fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) {
+        let FreeRegions {
+            indices,
+            free_region_map: _,
+        } = free_regions;
+
+        // For each free region X:
+        for (free_region, &variable) in indices {
+            // These should be free-region variables.
+            assert!(match self.definitions[variable].origin {
+                RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
+                _ => false,
+            });
+
+            // Initialize the name and a few other details.
+            self.definitions[variable].name = Some(free_region);
+
+            // Add all nodes in the CFG to `definition.value`.
+            for (block, block_data) in mir.basic_blocks().iter_enumerated() {
+                let liveness_constraint = &mut self.liveness_constraints[variable];
+                for statement_index in 0..block_data.statements.len() + 1 {
+                    let location = Location {
+                        block,
+                        statement_index,
+                    };
+                    liveness_constraint.add_point(location);
+                }
+            }
+
+            // Add `end(X)` into the set for X.
+            self.liveness_constraints[variable].add_free_region(variable);
+
+            debug!(
+                "init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`",
+                free_region,
+                variable,
+                self.liveness_constraints[variable],
+            );
+        }
+    }
+
+    /// Returns an iterator over all the region indices.
+    pub fn regions(&self) -> impl Iterator<Item = RegionVid> {
+        self.definitions.indices()
+    }
+
+    /// Returns true if the region `r` contains the point `p`.
+    ///
+    /// Until `solve()` executes, this value is not particularly meaningful.
+    pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool {
+        let inferred_values = self.inferred_values
+            .as_ref()
+            .expect("region values not yet inferred");
+        inferred_values[r].contains_point(p)
+    }
+
+    /// Returns access to the value of `r` for debugging purposes.
+    pub(super) fn region_value(&self, r: RegionVid) -> &fmt::Debug {
+        let inferred_values = self.inferred_values
+            .as_ref()
+            .expect("region values not yet inferred");
+        &inferred_values[r]
+    }
+
+    /// Indicates that the region variable `v` is live at the point `point`.
+    pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) {
+        debug!("add_live_point({:?}, {:?})", v, point);
+        assert!(self.inferred_values.is_none(), "values already inferred");
+        self.liveness_constraints[v].add_point(point);
+    }
+
+    /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
+    pub(super) fn add_outlives(
+        &mut self,
+        span: Span,
+        sup: RegionVid,
+        sub: RegionVid,
+        point: Location,
+    ) {
+        debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
+        assert!(self.inferred_values.is_none(), "values already inferred");
+        self.constraints.push(Constraint {
+            span,
+            sup,
+            sub,
+            point,
+        });
+    }
+
+    /// Perform region inference.
+    pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
+        assert!(self.inferred_values.is_none(), "values already inferred");
+
+        // Find the minimal regions that can solve the constraints. This is infallible.
+        self.propagate_constraints(mir);
+
+        // Now, see whether any of the constraints were too strong. In particular,
+        // we want to check for a case where a free region exceeded its bounds.
+        // Consider:
+        //
+        //     fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
+        //
+        // In this case, returning `x` requires `&'a u32 <: &'b u32`
+        // and hence we establish (transitively) a constraint that
+        // `'a: 'b`. The `propagate_constraints` code above will
+        // therefore add `end('a)` into the region for `'b` -- but we
+        // have no evidence that `'b` outlives `'a`, so we want to report
+        // an error.
+
+        // The free regions are always found in a prefix of the full list.
+        let free_region_definitions = self.definitions
+            .iter_enumerated()
+            .take_while(|(_, fr_definition)| fr_definition.name.is_some());
+
+        for (fr, fr_definition) in free_region_definitions {
+            self.check_free_region(infcx, fr, fr_definition);
+        }
+    }
+
+    fn check_free_region(
+        &self,
+        infcx: &InferCtxt<'_, '_, 'tcx>,
+        fr: RegionVid,
+        fr_definition: &RegionDefinition<'tcx>,
+    ) {
+        let inferred_values = self.inferred_values.as_ref().unwrap();
+        let fr_name = fr_definition.name.unwrap();
+        let fr_value = &inferred_values[fr];
+
+        // Find every region `o` such that `fr: o`
+        // (because `fr` includes `end(o)`).
+        for &outlived_fr in &fr_value.free_regions {
+            // `fr` includes `end(fr)`, that's not especially
+            // interesting.
+            if fr == outlived_fr {
+                continue;
+            }
+
+            let outlived_fr_definition = &self.definitions[outlived_fr];
+            let outlived_fr_name = outlived_fr_definition.name.unwrap();
+
+            // Check that `o <= fr`. If not, report an error.
+            if !self.free_region_map
+                .sub_free_regions(outlived_fr_name, fr_name)
+            {
+                // worst error msg ever
+                let blame_span = self.blame_span(fr, outlived_fr);
+                infcx.tcx.sess.span_err(
+                    blame_span,
+                    &format!(
+                        "free region `{}` does not outlive `{}`",
+                        fr_name,
+                        outlived_fr_name
+                    ),
+                );
+            }
+        }
+    }
+
+    /// Propagate the region constraints: this will grow the values
+    /// for each region variable until all the constraints are
+    /// satisfied. Note that some values may grow **too** large to be
+    /// feasible, but we check this later.
+    fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
+        let mut changed = true;
+        let mut dfs = Dfs::new(mir);
+
+        debug!("propagate_constraints()");
+        debug!("propagate_constraints: constraints={:#?}", {
+            let mut constraints: Vec<_> = self.constraints.iter().collect();
+            constraints.sort();
+            constraints
+        });
+
+        // The initial values for each region are derived from the liveness
+        // constraints we have accumulated.
+        let mut inferred_values = self.liveness_constraints.clone();
+
+        while changed {
+            changed = false;
+            debug!("propagate_constraints: --------------------");
+            for constraint in &self.constraints {
+                debug!("propagate_constraints: constraint={:?}", constraint);
+
+                let sub = &inferred_values[constraint.sub].clone();
+                let sup_value = &mut inferred_values[constraint.sup];
+
+                // Grow the value as needed to accommodate the
+                // outlives constraint.
+
+                if dfs.copy(sub, sup_value, constraint.point) {
+                    debug!("propagate_constraints:   sub={:?}", sub);
+                    debug!("propagate_constraints:   sup={:?}", sup_value);
+                    changed = true;
+                }
+            }
+            debug!("\n");
+        }
+
+        self.inferred_values = Some(inferred_values);
+    }
+
+    /// Tries to finds a good span to blame for the fact that `fr1`
+    /// contains `fr2`.
+    fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
+        // Find everything that influenced final value of `fr`.
+        let influenced_fr1 = self.dependencies(fr1);
+
+        // Try to find some outlives constraint `'X: fr2` where `'X`
+        // influenced `fr1`. Blame that.
+        //
+        // NB, this is a pretty bad choice most of the time. In
+        // particular, the connection between `'X` and `fr1` may not
+        // be obvious to the user -- not to mention the naive notion
+        // of dependencies, which doesn't account for the locations of
+        // contraints at all. But it will do for now.
+        for constraint in &self.constraints {
+            if constraint.sub == fr2 && influenced_fr1[constraint.sup] {
+                return constraint.span;
+            }
+        }
+
+        bug!(
+            "could not find any constraint to blame for {:?}: {:?}",
+            fr1,
+            fr2
+        );
+    }
+
+    /// Finds all regions whose values `'a` may depend on in some way.
+    /// Basically if there exists a constraint `'a: 'b @ P`, then `'b`
+    /// and `dependencies('b)` will be in the final set.
+    ///
+    /// Used during error reporting, extremely naive and inefficient.
+    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, bool> {
+        let mut result_set = IndexVec::from_elem(false, &self.definitions);
+        let mut changed = true;
+        result_set[r0] = true;
+
+        while changed {
+            changed = false;
+            for constraint in &self.constraints {
+                if result_set[constraint.sup] {
+                    if !result_set[constraint.sub] {
+                        result_set[constraint.sub] = true;
+                        changed = true;
+                    }
+                }
+            }
+        }
+
+        result_set
+    }
+}
+
+struct Dfs<'a, 'tcx: 'a> {
+    mir: &'a Mir<'tcx>,
+}
+
+impl<'a, 'tcx> Dfs<'a, 'tcx> {
+    fn new(mir: &'a Mir<'tcx>) -> Self {
+        Self { mir }
+    }
+
+    fn copy(
+        &mut self,
+        from_region: &Region,
+        to_region: &mut Region,
+        start_point: Location,
+    ) -> bool {
+        let mut changed = false;
+
+        let mut stack = vec![];
+        let mut visited = FxHashSet();
+
+        stack.push(start_point);
+        while let Some(p) = stack.pop() {
+            debug!("        dfs: p={:?}", p);
+
+            if !from_region.contains_point(p) {
+                debug!("            not in from-region");
+                continue;
+            }
+
+            if !visited.insert(p) {
+                debug!("            already visited");
+                continue;
+            }
+
+            changed |= to_region.add_point(p);
+
+            let block_data = &self.mir[p.block];
+            let successor_points = if p.statement_index < block_data.statements.len() {
+                vec![
+                    Location {
+                        statement_index: p.statement_index + 1,
+                        ..p
+                    },
+                ]
+            } else {
+                block_data
+                    .terminator()
+                    .successors()
+                    .iter()
+                    .map(|&basic_block| {
+                        Location {
+                            statement_index: 0,
+                            block: basic_block,
+                        }
+                    })
+                    .collect::<Vec<_>>()
+            };
+
+            if successor_points.is_empty() {
+                // If we reach the END point in the graph, then copy
+                // over any skolemized end points in the `from_region`
+                // and make sure they are included in the `to_region`.
+
+                debug!("        dfs: free_regions={:?}", from_region.free_regions);
+                for &fr in &from_region.free_regions {
+                    changed |= to_region.free_regions.insert(fr);
+                }
+            } else {
+                stack.extend(successor_points);
+            }
+        }
+
+        changed
+    }
+}
+
+impl<'tcx> RegionDefinition<'tcx> {
+    fn new(origin: RegionVariableOrigin) -> Self {
+        // Create a new region definition. Note that, for free
+        // regions, these fields get updated later in
+        // `init_free_regions`.
+        Self { origin, name: None }
+    }
+}
+
+impl fmt::Debug for Constraint {
+    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
+        write!(
+            formatter,
+            "({:?}: {:?} @ {:?}) due to {:?}",
+            self.sup,
+            self.sub,
+            self.point,
+            self.span
+        )
+    }
+}
diff --git a/src/librustc_mir/borrow_check/nll/renumber.rs b/src/librustc_mir/borrow_check/nll/renumber.rs
new file mode 100644 (file)
index 0000000..1076b77
--- /dev/null
@@ -0,0 +1,165 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc_data_structures::indexed_vec::{Idx, IndexVec};
+use rustc::ty::subst::Substs;
+use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
+use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
+use rustc::mir::visit::{MutVisitor, TyContext};
+use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
+
+use super::ToRegionVid;
+use super::free_regions::FreeRegions;
+
+/// Replaces all free regions appearing in the MIR with fresh
+/// inference variables, returning the number of variables created.
+pub fn renumber_mir<'a, 'gcx, 'tcx>(
+    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
+    free_regions: &FreeRegions<'tcx>,
+    mir: &mut Mir<'tcx>,
+) {
+    // Create inference variables for each of the free regions
+    // declared on the function signature.
+    let free_region_inference_vars = (0..free_regions.indices.len())
+        .map(RegionVid::new)
+        .map(|vid_expected| {
+            let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
+            assert_eq!(vid_expected, r.to_region_vid());
+            r
+        })
+        .collect();
+
+    debug!("renumber_mir()");
+    debug!("renumber_mir: free_regions={:#?}", free_regions);
+    debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
+
+    let mut visitor = NLLVisitor {
+        infcx,
+        free_regions,
+        free_region_inference_vars,
+        arg_count: mir.arg_count,
+    };
+    visitor.visit_mir(mir);
+}
+
+struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
+    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
+    free_regions: &'a FreeRegions<'tcx>,
+    free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
+    arg_count: usize,
+}
+
+impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
+    /// Replaces all regions appearing in `value` with fresh inference
+    /// variables. This is what we do for almost the entire MIR, with
+    /// the exception of the declared types of our arguments.
+    fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!("renumber_regions(value={:?})", value);
+
+        self.infcx
+            .tcx
+            .fold_regions(value, &mut false, |_region, _depth| {
+                let origin = NLLRegionVariableOrigin::Inferred(ty_context);
+                self.infcx.next_nll_region_var(origin)
+            })
+    }
+
+    /// Renumbers the regions appearing in `value`, but those regions
+    /// are expected to be free regions from the function signature.
+    fn renumber_free_regions<T>(&mut self, value: &T) -> T
+    where
+        T: TypeFoldable<'tcx>,
+    {
+        debug!("renumber_free_regions(value={:?})", value);
+
+        self.infcx
+            .tcx
+            .fold_regions(value, &mut false, |region, _depth| {
+                let index = self.free_regions.indices[&region];
+                self.free_region_inference_vars[index]
+            })
+    }
+
+    fn is_argument_or_return_slot(&self, local: Local) -> bool {
+        // The first argument is return slot, next N are arguments.
+        local.index() <= self.arg_count
+    }
+}
+
+impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
+    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
+        let is_arg = match ty_context {
+            TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
+            TyContext::ReturnTy(..) => true,
+            TyContext::Location(..) => false,
+        };
+        debug!(
+            "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})",
+            ty,
+            is_arg,
+            ty_context
+        );
+
+        let old_ty = *ty;
+        *ty = if is_arg {
+            self.renumber_free_regions(&old_ty)
+        } else {
+            self.renumber_regions(ty_context, &old_ty)
+        };
+        debug!("visit_ty: ty={:?}", ty);
+    }
+
+    fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
+        debug!("visit_substs(substs={:?}, location={:?})", substs, location);
+
+        let ty_context = TyContext::Location(location);
+        *substs = self.renumber_regions(ty_context, &{ *substs });
+
+        debug!("visit_substs: substs={:?}", substs);
+    }
+
+    fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) {
+        debug!("visit_region(region={:?}, location={:?})", region, location);
+
+        let old_region = *region;
+        let ty_context = TyContext::Location(location);
+        *region = self.renumber_regions(ty_context, &old_region);
+
+        debug!("visit_region: region={:?}", region);
+    }
+
+    fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
+        debug!(
+            "visit_closure_substs(substs={:?}, location={:?})",
+            substs,
+            location
+        );
+
+        let ty_context = TyContext::Location(location);
+        *substs = self.renumber_regions(ty_context, substs);
+
+        debug!("visit_closure_substs: substs={:?}", substs);
+    }
+
+    fn visit_statement(
+        &mut self,
+        block: BasicBlock,
+        statement: &mut Statement<'tcx>,
+        location: Location,
+    ) {
+        if let StatementKind::EndRegion(_) = statement.kind {
+            statement.kind = StatementKind::Nop;
+        }
+        self.super_statement(block, statement, location);
+    }
+}
diff --git a/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs b/src/librustc_mir/borrow_check/nll/subtype_constraint_generation.rs
new file mode 100644 (file)
index 0000000..c1850c7
--- /dev/null
@@ -0,0 +1,112 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+use rustc::mir::Mir;
+use rustc::infer::region_constraints::Constraint;
+use rustc::infer::region_constraints::RegionConstraintData;
+use rustc::ty;
+use transform::type_check::MirTypeckRegionConstraints;
+use transform::type_check::OutlivesSet;
+
+use super::free_regions::FreeRegions;
+use super::region_infer::RegionInferenceContext;
+
+/// When the MIR type-checker executes, it validates all the types in
+/// the MIR, and in the process generates a set of constraints that
+/// must hold regarding the regions in the MIR, along with locations
+/// *where* they must hold. This code takes those constriants and adds
+/// them into the NLL `RegionInferenceContext`.
+pub(super) fn generate<'tcx>(
+    regioncx: &mut RegionInferenceContext<'tcx>,
+    free_regions: &FreeRegions<'tcx>,
+    mir: &Mir<'tcx>,
+    constraints: &MirTypeckRegionConstraints<'tcx>,
+) {
+    SubtypeConstraintGenerator {
+        regioncx,
+        free_regions,
+        mir,
+    }.generate(constraints);
+}
+
+struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
+    regioncx: &'cx mut RegionInferenceContext<'tcx>,
+    free_regions: &'cx FreeRegions<'tcx>,
+    mir: &'cx Mir<'tcx>,
+}
+
+impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
+    fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) {
+        let MirTypeckRegionConstraints {
+            liveness_set,
+            outlives_sets,
+        } = constraints;
+
+        debug!(
+            "generate(liveness_set={} items, outlives_sets={} items)",
+            liveness_set.len(),
+            outlives_sets.len()
+        );
+
+        for (region, location) in liveness_set {
+            debug!("generate: {:#?} is live at {:#?}", region, location);
+            let region_vid = self.to_region_vid(region);
+            self.regioncx.add_live_point(region_vid, *location);
+        }
+
+        for OutlivesSet { locations, data } in outlives_sets {
+            debug!("generate: constraints at: {:#?}", locations);
+            let RegionConstraintData {
+                constraints,
+                verifys,
+                givens,
+            } = data;
+
+            for constraint in constraints.keys() {
+                debug!("generate: constraint: {:?}", constraint);
+                let (a_vid, b_vid) = match constraint {
+                    Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid),
+                    Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid),
+                    Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)),
+                    Constraint::RegSubReg(a_r, b_r) => {
+                        (self.to_region_vid(a_r), self.to_region_vid(b_r))
+                    }
+                };
+
+                // We have the constraint that `a_vid <= b_vid`. Add
+                // `b_vid: a_vid` to our region checker. Note that we
+                // reverse direction, because `regioncx` talks about
+                // "outlives" (`>=`) whereas the region constraints
+                // talk about `<=`.
+                let span = self.mir.source_info(locations.from_location).span;
+                self.regioncx
+                    .add_outlives(span, b_vid, a_vid, locations.at_location);
+            }
+
+            assert!(verifys.is_empty(), "verifys not yet implemented");
+            assert!(
+                givens.is_empty(),
+                "MIR type-checker does not use givens (thank goodness)"
+            );
+        }
+    }
+
+    fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
+        // Every region that we see in the constraints came from the
+        // MIR or from the parameter environment. If the former, it
+        // will be a region variable.  If the latter, it will be in
+        // the set of free regions *somewhere*.
+        if let ty::ReVar(vid) = r {
+            *vid
+        } else {
+            self.free_regions.indices[&r]
+        }
+    }
+}
index 19ab45dda95453a3000ffde578f369f647a05e77..286ca768b16bf1639b82cbe512571d88cc5876ac 100644 (file)
@@ -21,8 +21,8 @@
 
 use dataflow::{BitDenotation, BlockSets, DataflowOperator};
 pub use dataflow::indexes::BorrowIndex;
-use transform::nll::region_infer::RegionInferenceContext;
-use transform::nll::ToRegionVid;
+use borrow_check::nll::region_infer::RegionInferenceContext;
+use borrow_check::nll::ToRegionVid;
 
 use syntax_pos::Span;
 
@@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
     location_map: FxHashMap<Location, BorrowIndex>,
     region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
     region_span_map: FxHashMap<RegionKind, Span>,
-    nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>,
+    nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>,
 }
 
 // temporarily allow some dead fields: `kind` and `region` will be
@@ -69,7 +69,7 @@ fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
 impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
     pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
                mir: &'a Mir<'tcx>,
-               nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>)
+               nonlexical_regioncx: Option<RegionInferenceContext<'tcx>>)
                -> Self {
         let mut visitor = GatherBorrows {
             tcx,
@@ -156,7 +156,7 @@ pub fn opt_region_end_span(&self, region: &Region) -> Option<Span> {
     fn kill_loans_out_of_scope_at_location(&self,
                                            sets: &mut BlockSets<BorrowIndex>,
                                            location: Location) {
-        if let Some(regioncx) = self.nonlexical_regioncx {
+        if let Some(ref regioncx) = self.nonlexical_regioncx {
             for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
                 let borrow_region = borrow_data.region.to_region_vid();
                 if !regioncx.region_contains_point(borrow_region, location) {
index 294f48178a8ac6d98630ef1b6fad808152d43ddf..9d91e1344dc373b16f27c6a2b58a3ea47f0e516d 100644 (file)
@@ -263,6 +263,10 @@ pub fn find(&self, place: &Place<'tcx>) -> LookupResult {
             }
         }
     }
+
+    pub fn find_local(&self, local: Local) -> MovePathIndex {
+        self.locals[local]
+    }
 }
 
 #[derive(Debug)]
index 830838c60375898d32ee913936a92465c276db74..fb9daf07c71dc735a894c260d482496bec3e0a3c 100644 (file)
@@ -43,7 +43,6 @@
 pub mod copy_prop;
 pub mod generator;
 pub mod inline;
-pub mod nll;
 pub mod lower_128bit;
 
 pub(crate) fn provide(providers: &mut Providers) {
diff --git a/src/librustc_mir/transform/nll/constraint_generation.rs b/src/librustc_mir/transform/nll/constraint_generation.rs
deleted file mode 100644 (file)
index 73d5a61..0000000
+++ /dev/null
@@ -1,241 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc::hir;
-use rustc::mir::{Location, Place, Mir, Rvalue};
-use rustc::mir::visit::Visitor;
-use rustc::mir::Place::Projection;
-use rustc::mir::{PlaceProjection, ProjectionElem};
-use rustc::infer::InferCtxt;
-use rustc::traits::{self, ObligationCause};
-use rustc::ty::{self, Ty};
-use rustc::ty::fold::TypeFoldable;
-use rustc::util::common::ErrorReported;
-use rustc_data_structures::fx::FxHashSet;
-use syntax::codemap::DUMMY_SP;
-
-use super::LivenessResults;
-use super::ToRegionVid;
-use super::region_infer::RegionInferenceContext;
-
-pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    regioncx: &mut RegionInferenceContext<'tcx>,
-    mir: &Mir<'tcx>,
-    param_env: ty::ParamEnv<'tcx>,
-    liveness: &LivenessResults,
-) {
-    ConstraintGeneration {
-        infcx,
-        regioncx,
-        mir,
-        liveness,
-        param_env,
-    }.add_constraints();
-}
-
-struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
-    infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
-    regioncx: &'cx mut RegionInferenceContext<'tcx>,
-    mir: &'cx Mir<'tcx>,
-    liveness: &'cx LivenessResults,
-    param_env: ty::ParamEnv<'tcx>,
-}
-
-impl<'cx, 'gcx, 'tcx> ConstraintGeneration<'cx, 'gcx, 'tcx> {
-    fn add_constraints(&mut self) {
-        self.add_liveness_constraints();
-        self.add_borrow_constraints();
-    }
-
-    /// Liveness constraints:
-    ///
-    /// > If a variable V is live at point P, then all regions R in the type of V
-    /// > must include the point P.
-    fn add_liveness_constraints(&mut self) {
-        debug!("add_liveness_constraints()");
-        for bb in self.mir.basic_blocks().indices() {
-            debug!("add_liveness_constraints: bb={:?}", bb);
-
-            self.liveness
-                .regular
-                .simulate_block(self.mir, bb, |location, live_locals| {
-                    for live_local in live_locals.iter() {
-                        let live_local_ty = self.mir.local_decls[live_local].ty;
-                        self.add_regular_live_constraint(live_local_ty, location);
-                    }
-                });
-
-            self.liveness
-                .drop
-                .simulate_block(self.mir, bb, |location, live_locals| {
-                    for live_local in live_locals.iter() {
-                        let live_local_ty = self.mir.local_decls[live_local].ty;
-                        self.add_drop_live_constraint(live_local_ty, location);
-                    }
-                });
-        }
-    }
-
-    /// Some variable with type `live_ty` is "regular live" at
-    /// `location` -- i.e., it may be used later. This means that all
-    /// regions appearing in the type `live_ty` must be live at
-    /// `location`.
-    fn add_regular_live_constraint<T>(&mut self, live_ty: T, location: Location)
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        debug!(
-            "add_regular_live_constraint(live_ty={:?}, location={:?})",
-            live_ty,
-            location
-        );
-
-        self.infcx
-            .tcx
-            .for_each_free_region(&live_ty, |live_region| {
-                let vid = live_region.to_region_vid();
-                self.regioncx.add_live_point(vid, location);
-            });
-    }
-
-    /// Some variable with type `live_ty` is "drop live" at `location`
-    /// -- i.e., it may be dropped later. This means that *some* of
-    /// the regions in its type must be live at `location`. The
-    /// precise set will depend on the dropck constraints, and in
-    /// particular this takes `#[may_dangle]` into account.
-    fn add_drop_live_constraint(&mut self, dropped_ty: Ty<'tcx>, location: Location) {
-        debug!(
-            "add_drop_live_constraint(dropped_ty={:?}, location={:?})",
-            dropped_ty,
-            location
-        );
-
-        let tcx = self.infcx.tcx;
-        let mut types = vec![(dropped_ty, 0)];
-        let mut known = FxHashSet();
-        while let Some((ty, depth)) = types.pop() {
-            let span = DUMMY_SP; // FIXME
-            let result = match tcx.dtorck_constraint_for_ty(span, dropped_ty, depth, ty) {
-                Ok(result) => result,
-                Err(ErrorReported) => {
-                    continue;
-                }
-            };
-
-            let ty::DtorckConstraint {
-                outlives,
-                dtorck_types,
-            } = result;
-
-            // All things in the `outlives` array may be touched by
-            // the destructor and must be live at this point.
-            for outlive in outlives {
-                if let Some(ty) = outlive.as_type() {
-                    self.add_regular_live_constraint(ty, location);
-                } else if let Some(r) = outlive.as_region() {
-                    self.add_regular_live_constraint(r, location);
-                } else {
-                    bug!()
-                }
-            }
-
-            // However, there may also be some types that
-            // `dtorck_constraint_for_ty` could not resolve (e.g.,
-            // associated types and parameters). We need to normalize
-            // associated types here and possibly recursively process.
-            for ty in dtorck_types {
-                let cause = ObligationCause::dummy();
-                // We know that our original `dropped_ty` is well-formed,
-                // so region obligations resulting from this normalization
-                // should always hold.
-                //
-                // Therefore we ignore them instead of trying to match
-                // them up with a location.
-                let fulfillcx = traits::FulfillmentContext::new_ignoring_regions();
-                match traits::fully_normalize_with_fulfillcx(
-                    self.infcx, fulfillcx, cause, self.param_env, &ty
-                ) {
-                    Ok(ty) => match ty.sty {
-                        ty::TyParam(..) | ty::TyProjection(..) | ty::TyAnon(..) => {
-                            self.add_regular_live_constraint(ty, location);
-                        }
-
-                        _ => if known.insert(ty) {
-                            types.push((ty, depth + 1));
-                        },
-                    },
-
-                    Err(errors) => {
-                        self.infcx.report_fulfillment_errors(&errors, None);
-                    }
-                }
-            }
-        }
-    }
-
-    fn add_borrow_constraints(&mut self) {
-        self.visit_mir(self.mir);
-    }
-
-    fn add_reborrow_constraint(
-        &mut self,
-        location: Location,
-        borrow_region: ty::Region<'tcx>,
-        borrowed_place: &Place<'tcx>,
-    ) {
-        if let Projection(ref proj) = *borrowed_place {
-            let PlaceProjection { ref base, ref elem } = **proj;
-
-            if let ProjectionElem::Deref = *elem {
-                let tcx = self.infcx.tcx;
-                let base_ty = base.ty(self.mir, tcx).to_ty(tcx);
-                let base_sty = &base_ty.sty;
-
-                if let ty::TyRef(base_region, ty::TypeAndMut{ ty: _, mutbl }) = *base_sty {
-                    match mutbl {
-                        hir::Mutability::MutImmutable => { },
-
-                        hir::Mutability::MutMutable => {
-                            self.add_reborrow_constraint(location, borrow_region, base);
-                        },
-                    }
-
-                    let span = self.mir.source_info(location).span;
-                    self.regioncx.add_outlives(span,
-                                               base_region.to_region_vid(),
-                                               borrow_region.to_region_vid(),
-                                               location.successor_within_block());
-                }
-            }
-        }
-    }
-}
-
-impl<'cx, 'gcx, 'tcx> Visitor<'tcx> for ConstraintGeneration<'cx, 'gcx, 'tcx> {
-    fn visit_rvalue(&mut self,
-                    rvalue: &Rvalue<'tcx>,
-                    location: Location) {
-        debug!("visit_rvalue(rvalue={:?}, location={:?})", rvalue, location);
-
-        // Look for an rvalue like:
-        //
-        //     & L
-        //
-        // where L is the path that is borrowed. In that case, we have
-        // to add the reborrow constraints (which don't fall out
-        // naturally from the type-checker).
-        if let Rvalue::Ref(region, _bk, ref borrowed_place) = *rvalue {
-            self.add_reborrow_constraint(location, region, borrowed_place);
-        }
-
-        self.super_rvalue(rvalue, location);
-    }
-}
diff --git a/src/librustc_mir/transform/nll/free_regions.rs b/src/librustc_mir/transform/nll/free_regions.rs
deleted file mode 100644 (file)
index 92a8a71..0000000
+++ /dev/null
@@ -1,90 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-//! Code to extract the free regions declared on a function and the
-//! relationships between them. For example:
-//!
-//! ```
-//! fn foo<'a, 'b, 'c: 'b>() { }
-//! ```
-//!
-//! here we would be returning a map assigning each of `{'a, 'b, 'c}`
-//! to an index, as well as the `FreeRegionMap` which can compute
-//! relationships between them.
-//!
-//! The code in this file doesn't *do anything* with those results; it
-//! just returns them for other code to use.
-
-use rustc::hir::def_id::DefId;
-use rustc::infer::InferCtxt;
-use rustc::middle::free_region::FreeRegionMap;
-use rustc::ty::{self, RegionVid};
-use rustc::ty::subst::Substs;
-use rustc::util::nodemap::FxHashMap;
-use rustc_data_structures::indexed_vec::Idx;
-
-#[derive(Debug)]
-pub struct FreeRegions<'tcx> {
-    /// Given a free region defined on this function (either early- or
-    /// late-bound), this maps it to its internal region index. When
-    /// the region context is created, the first N variables will be
-    /// created based on these indices.
-    pub indices: FxHashMap<ty::Region<'tcx>, RegionVid>,
-
-    /// The map from the typeck tables telling us how to relate free regions.
-    pub free_region_map: &'tcx FreeRegionMap<'tcx>,
-}
-
-pub fn free_regions<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    item_def_id: DefId,
-) -> FreeRegions<'tcx> {
-    debug!("free_regions(item_def_id={:?})", item_def_id);
-
-    let mut indices = FxHashMap();
-
-    // `'static` is always free.
-    insert_free_region(&mut indices, infcx.tcx.types.re_static);
-
-    // Extract the early regions.
-    let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
-    for item_subst in item_substs {
-        if let Some(region) = item_subst.as_region() {
-            insert_free_region(&mut indices, region);
-        }
-    }
-
-    // Extract the late-bound regions. Use the liberated fn sigs,
-    // where the late-bound regions will have been converted into free
-    // regions, and add them to the map.
-    let item_id = infcx.tcx.hir.as_local_node_id(item_def_id).unwrap();
-    let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
-    let tables = infcx.tcx.typeck_tables_of(item_def_id);
-    let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
-    infcx
-        .tcx
-        .for_each_free_region(&fn_sig.inputs_and_output, |region| {
-            if let ty::ReFree(_) = *region {
-                insert_free_region(&mut indices, region);
-            }
-        });
-
-    debug!("free_regions: indices={:#?}", indices);
-
-    FreeRegions { indices, free_region_map: &tables.free_region_map }
-}
-
-fn insert_free_region<'tcx>(
-    free_regions: &mut FxHashMap<ty::Region<'tcx>, RegionVid>,
-    region: ty::Region<'tcx>,
-) {
-    let next = RegionVid::new(free_regions.len());
-    free_regions.entry(region).or_insert(next);
-}
diff --git a/src/librustc_mir/transform/nll/mod.rs b/src/librustc_mir/transform/nll/mod.rs
deleted file mode 100644 (file)
index 147f061..0000000
+++ /dev/null
@@ -1,204 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc::hir::def_id::DefId;
-use rustc::mir::Mir;
-use rustc::infer::InferCtxt;
-use rustc::ty::{self, RegionKind, RegionVid};
-use rustc::util::nodemap::FxHashMap;
-use std::collections::BTreeSet;
-use transform::MirSource;
-use transform::type_check;
-use util::liveness::{self, LivenessMode, LivenessResult, LocalSet};
-
-use util as mir_util;
-use self::mir_util::PassWhere;
-
-mod constraint_generation;
-mod subtype_constraint_generation;
-mod free_regions;
-
-pub(crate) mod region_infer;
-use self::region_infer::RegionInferenceContext;
-
-mod renumber;
-
-/// Computes the (non-lexical) regions from the input MIR.
-///
-/// This may result in errors being reported.
-pub fn compute_regions<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    def_id: DefId,
-    param_env: ty::ParamEnv<'gcx>,
-    mir: &mut Mir<'tcx>,
-) -> RegionInferenceContext<'tcx> {
-    // Compute named region information.
-    let free_regions = &free_regions::free_regions(infcx, def_id);
-
-    // Replace all regions with fresh inference variables.
-    renumber::renumber_mir(infcx, free_regions, mir);
-
-    // Run the MIR type-checker.
-    let mir_node_id = infcx.tcx.hir.as_local_node_id(def_id).unwrap();
-    let constraint_sets = &type_check::type_check(infcx, mir_node_id, param_env, mir);
-
-    // Create the region inference context, taking ownership of the region inference
-    // data that was contained in `infcx`.
-    let var_origins = infcx.take_region_var_origins();
-    let mut regioncx = RegionInferenceContext::new(var_origins, free_regions, mir);
-    subtype_constraint_generation::generate(&mut regioncx, free_regions, mir, constraint_sets);
-
-    // Compute what is live where.
-    let liveness = &LivenessResults {
-        regular: liveness::liveness_of_locals(
-            &mir,
-            LivenessMode {
-                include_regular_use: true,
-                include_drops: false,
-            },
-        ),
-
-        drop: liveness::liveness_of_locals(
-            &mir,
-            LivenessMode {
-                include_regular_use: false,
-                include_drops: true,
-            },
-        ),
-    };
-
-    // Generate non-subtyping constraints.
-    constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, param_env, liveness);
-
-    // Solve the region constraints.
-    regioncx.solve(infcx, &mir);
-
-    // Dump MIR results into a file, if that is enabled. This let us
-    // write unit-tests.
-    dump_mir_results(infcx, liveness, MirSource::item(def_id), &mir, &regioncx);
-
-    regioncx
-}
-
-struct LivenessResults {
-    regular: LivenessResult,
-    drop: LivenessResult,
-}
-
-fn dump_mir_results<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    liveness: &LivenessResults,
-    source: MirSource,
-    mir: &Mir<'tcx>,
-    regioncx: &RegionInferenceContext,
-) {
-    if !mir_util::dump_enabled(infcx.tcx, "nll", source) {
-        return;
-    }
-
-    let regular_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
-        .indices()
-        .flat_map(|bb| {
-            let mut results = vec![];
-            liveness
-                .regular
-                .simulate_block(&mir, bb, |location, local_set| {
-                    results.push((location, local_set.clone()));
-                });
-            results
-        })
-        .collect();
-
-    let drop_liveness_per_location: FxHashMap<_, _> = mir.basic_blocks()
-        .indices()
-        .flat_map(|bb| {
-            let mut results = vec![];
-            liveness
-                .drop
-                .simulate_block(&mir, bb, |location, local_set| {
-                    results.push((location, local_set.clone()));
-                });
-            results
-        })
-        .collect();
-
-    mir_util::dump_mir(infcx.tcx, None, "nll", &0, source, mir, |pass_where, out| {
-        match pass_where {
-            // Before the CFG, dump out the values for each region variable.
-            PassWhere::BeforeCFG => for region in regioncx.regions() {
-                writeln!(out, "| {:?}: {:?}", region, regioncx.region_value(region))?;
-            },
-
-            // Before each basic block, dump out the values
-            // that are live on entry to the basic block.
-            PassWhere::BeforeBlock(bb) => {
-                let s = live_variable_set(&liveness.regular.ins[bb], &liveness.drop.ins[bb]);
-                writeln!(out, "    | Live variables on entry to {:?}: {}", bb, s)?;
-            }
-
-            PassWhere::InCFG(location) => {
-                let s = live_variable_set(
-                    &regular_liveness_per_location[&location],
-                    &drop_liveness_per_location[&location],
-                );
-                writeln!(out, "            | Live variables at {:?}: {}", location, s)?;
-            }
-
-            PassWhere::AfterCFG => {}
-        }
-        Ok(())
-    });
-}
-
-/// Right now, we piggy back on the `ReVar` to store our NLL inference
-/// regions. These are indexed with `RegionVid`. This method will
-/// assert that the region is a `ReVar` and extract its interal index.
-/// This is reasonable because in our MIR we replace all free regions
-/// with inference variables.
-pub trait ToRegionVid {
-    fn to_region_vid(&self) -> RegionVid;
-}
-
-impl ToRegionVid for RegionKind {
-    fn to_region_vid(&self) -> RegionVid {
-        if let &ty::ReVar(vid) = self {
-            vid
-        } else {
-            bug!("region is not an ReVar: {:?}", self)
-        }
-    }
-}
-
-fn live_variable_set(regular: &LocalSet, drops: &LocalSet) -> String {
-    // sort and deduplicate:
-    let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect();
-
-    // construct a string with each local, including `(drop)` if it is
-    // only dropped, versus a regular use.
-    let mut string = String::new();
-    for local in all_locals {
-        string.push_str(&format!("{:?}", local));
-
-        if !regular.contains(&local) {
-            assert!(drops.contains(&local));
-            string.push_str(" (drop)");
-        }
-
-        string.push_str(", ");
-    }
-
-    let len = if string.is_empty() {
-        0
-    } else {
-        string.len() - 2
-    };
-
-    format!("[{}]", &string[..len])
-}
diff --git a/src/librustc_mir/transform/nll/region_infer.rs b/src/librustc_mir/transform/nll/region_infer.rs
deleted file mode 100644 (file)
index f60bd3c..0000000
+++ /dev/null
@@ -1,512 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use super::free_regions::FreeRegions;
-use rustc::infer::InferCtxt;
-use rustc::infer::RegionVariableOrigin;
-use rustc::infer::NLLRegionVariableOrigin;
-use rustc::infer::region_constraints::VarOrigins;
-use rustc::middle::free_region::FreeRegionMap;
-use rustc::mir::{Location, Mir};
-use rustc::ty::{self, RegionVid};
-use rustc_data_structures::indexed_vec::IndexVec;
-use rustc_data_structures::fx::FxHashSet;
-use std::collections::BTreeSet;
-use std::fmt;
-use syntax_pos::Span;
-
-pub struct RegionInferenceContext<'tcx> {
-    /// Contains the definition for every region variable.  Region
-    /// variables are identified by their index (`RegionVid`). The
-    /// definition contains information about where the region came
-    /// from as well as its final inferred value.
-    definitions: IndexVec<RegionVid, RegionDefinition<'tcx>>,
-
-    /// The liveness constraints added to each region. For most
-    /// regions, these start out empty and steadily grow, though for
-    /// each free region R they start out containing the entire CFG
-    /// and `end(R)`.
-    liveness_constraints: IndexVec<RegionVid, Region>,
-
-    /// The final inferred values of the inference variables; `None`
-    /// until `solve` is invoked.
-    inferred_values: Option<IndexVec<RegionVid, Region>>,
-
-    /// The constraints we have accumulated and used during solving.
-    constraints: Vec<Constraint>,
-
-    free_region_map: &'tcx FreeRegionMap<'tcx>,
-}
-
-struct RegionDefinition<'tcx> {
-    /// Why we created this variable. Mostly these will be
-    /// `RegionVariableOrigin::NLL`, but some variables get created
-    /// elsewhere in the code with other causes (e.g., instantiation
-    /// late-bound-regions).
-    origin: RegionVariableOrigin,
-
-    /// If this is a free-region, then this is `Some(X)` where `X` is
-    /// the name of the region.
-    name: Option<ty::Region<'tcx>>,
-}
-
-/// The value of an individual region variable. Region variables
-/// consist of a set of points in the CFG as well as a set of "free
-/// regions", which are sometimes written as `end(R)`. These
-/// correspond to the named lifetimes and refer to portions of the
-/// caller's control-flow graph -- specifically some portion that can
-/// be reached after we return.
-#[derive(Clone, Default, PartialEq, Eq)]
-struct Region {
-    points: BTreeSet<Location>,
-    free_regions: BTreeSet<RegionVid>,
-}
-
-impl fmt::Debug for Region {
-    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        formatter
-            .debug_set()
-            .entries(&self.points)
-            .entries(&self.free_regions)
-            .finish()
-    }
-}
-
-impl Region {
-    fn add_point(&mut self, point: Location) -> bool {
-        self.points.insert(point)
-    }
-
-    fn add_free_region(&mut self, region: RegionVid) -> bool {
-        self.free_regions.insert(region)
-    }
-
-    fn contains_point(&self, point: Location) -> bool {
-        self.points.contains(&point)
-    }
-}
-
-#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
-pub struct Constraint {
-    // NB. The ordering here is not significant for correctness, but
-    // it is for convenience. Before we dump the constraints in the
-    // debugging logs, we sort them, and we'd like the "super region"
-    // to be first, etc. (In particular, span should remain last.)
-    /// The region SUP must outlive SUB...
-    sup: RegionVid,
-
-    /// Region that must be outlived.
-    sub: RegionVid,
-
-    /// At this location.
-    point: Location,
-
-    /// Where did this constraint arise?
-    span: Span,
-}
-
-impl<'tcx> RegionInferenceContext<'tcx> {
-    /// Creates a new region inference context with a total of
-    /// `num_region_variables` valid inference variables; the first N
-    /// of those will be constant regions representing the free
-    /// regions defined in `free_regions`.
-    pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) -> Self {
-        let num_region_variables = var_origins.len();
-
-        // Create a RegionDefinition for each inference variable.
-        let definitions = var_origins
-            .into_iter()
-            .map(|origin| RegionDefinition::new(origin))
-            .collect();
-
-        let mut result = Self {
-            definitions: definitions,
-            liveness_constraints: IndexVec::from_elem_n(Region::default(), num_region_variables),
-            inferred_values: None,
-            constraints: Vec::new(),
-            free_region_map: free_regions.free_region_map,
-        };
-
-        result.init_free_regions(free_regions, mir);
-
-        result
-    }
-
-    /// Initializes the region variables for each free region
-    /// (lifetime parameter). The first N variables always correspond
-    /// to the free regions appearing in the function signature (both
-    /// named and anonymous) and where clauses. This function iterates
-    /// over those regions and initializes them with minimum values.
-    ///
-    /// For example:
-    ///
-    ///     fn foo<'a, 'b>(..) where 'a: 'b
-    ///
-    /// would initialize two variables like so:
-    ///
-    ///     R0 = { CFG, R0 } // 'a
-    ///     R1 = { CFG, R0, R1 } // 'b
-    ///
-    /// Here, R0 represents `'a`, and it contains (a) the entire CFG
-    /// and (b) any free regions that it outlives, which in this case
-    /// is just itself. R1 (`'b`) in contrast also outlives `'a` and
-    /// hence contains R0 and R1.
-    fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) {
-        let FreeRegions {
-            indices,
-            free_region_map: _,
-        } = free_regions;
-
-        // For each free region X:
-        for (free_region, &variable) in indices {
-            // These should be free-region variables.
-            assert!(match self.definitions[variable].origin {
-                RegionVariableOrigin::NLL(NLLRegionVariableOrigin::FreeRegion) => true,
-                _ => false,
-            });
-
-            // Initialize the name and a few other details.
-            self.definitions[variable].name = Some(free_region);
-
-            // Add all nodes in the CFG to `definition.value`.
-            for (block, block_data) in mir.basic_blocks().iter_enumerated() {
-                let liveness_constraint = &mut self.liveness_constraints[variable];
-                for statement_index in 0..block_data.statements.len() + 1 {
-                    let location = Location {
-                        block,
-                        statement_index,
-                    };
-                    liveness_constraint.add_point(location);
-                }
-            }
-
-            // Add `end(X)` into the set for X.
-            self.liveness_constraints[variable].add_free_region(variable);
-
-            debug!(
-                "init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`",
-                free_region,
-                variable,
-                self.liveness_constraints[variable],
-            );
-        }
-    }
-
-    /// Returns an iterator over all the region indices.
-    pub fn regions(&self) -> impl Iterator<Item = RegionVid> {
-        self.definitions.indices()
-    }
-
-    /// Returns true if the region `r` contains the point `p`.
-    ///
-    /// Until `solve()` executes, this value is not particularly meaningful.
-    pub fn region_contains_point(&self, r: RegionVid, p: Location) -> bool {
-        let inferred_values = self.inferred_values
-            .as_ref()
-            .expect("region values not yet inferred");
-        inferred_values[r].contains_point(p)
-    }
-
-    /// Returns access to the value of `r` for debugging purposes.
-    pub(super) fn region_value(&self, r: RegionVid) -> &fmt::Debug {
-        let inferred_values = self.inferred_values
-            .as_ref()
-            .expect("region values not yet inferred");
-        &inferred_values[r]
-    }
-
-    /// Indicates that the region variable `v` is live at the point `point`.
-    pub(super) fn add_live_point(&mut self, v: RegionVid, point: Location) {
-        debug!("add_live_point({:?}, {:?})", v, point);
-        assert!(self.inferred_values.is_none(), "values already inferred");
-        self.liveness_constraints[v].add_point(point);
-    }
-
-    /// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
-    pub(super) fn add_outlives(
-        &mut self,
-        span: Span,
-        sup: RegionVid,
-        sub: RegionVid,
-        point: Location,
-    ) {
-        debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
-        assert!(self.inferred_values.is_none(), "values already inferred");
-        self.constraints.push(Constraint {
-            span,
-            sup,
-            sub,
-            point,
-        });
-    }
-
-    /// Perform region inference.
-    pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
-        assert!(self.inferred_values.is_none(), "values already inferred");
-
-        // Find the minimal regions that can solve the constraints. This is infallible.
-        self.propagate_constraints(mir);
-
-        // Now, see whether any of the constraints were too strong. In particular,
-        // we want to check for a case where a free region exceeded its bounds.
-        // Consider:
-        //
-        //     fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
-        //
-        // In this case, returning `x` requires `&'a u32 <: &'b u32`
-        // and hence we establish (transitively) a constraint that
-        // `'a: 'b`. The `propagate_constraints` code above will
-        // therefore add `end('a)` into the region for `'b` -- but we
-        // have no evidence that `'b` outlives `'a`, so we want to report
-        // an error.
-
-        // The free regions are always found in a prefix of the full list.
-        let free_region_definitions = self.definitions
-            .iter_enumerated()
-            .take_while(|(_, fr_definition)| fr_definition.name.is_some());
-
-        for (fr, fr_definition) in free_region_definitions {
-            self.check_free_region(infcx, fr, fr_definition);
-        }
-    }
-
-    fn check_free_region(
-        &self,
-        infcx: &InferCtxt<'_, '_, 'tcx>,
-        fr: RegionVid,
-        fr_definition: &RegionDefinition<'tcx>,
-    ) {
-        let inferred_values = self.inferred_values.as_ref().unwrap();
-        let fr_name = fr_definition.name.unwrap();
-        let fr_value = &inferred_values[fr];
-
-        // Find every region `o` such that `fr: o`
-        // (because `fr` includes `end(o)`).
-        for &outlived_fr in &fr_value.free_regions {
-            // `fr` includes `end(fr)`, that's not especially
-            // interesting.
-            if fr == outlived_fr {
-                continue;
-            }
-
-            let outlived_fr_definition = &self.definitions[outlived_fr];
-            let outlived_fr_name = outlived_fr_definition.name.unwrap();
-
-            // Check that `o <= fr`. If not, report an error.
-            if !self.free_region_map
-                .sub_free_regions(outlived_fr_name, fr_name)
-            {
-                // worst error msg ever
-                let blame_span = self.blame_span(fr, outlived_fr);
-                infcx.tcx.sess.span_err(
-                    blame_span,
-                    &format!(
-                        "free region `{}` does not outlive `{}`",
-                        fr_name,
-                        outlived_fr_name
-                    ),
-                );
-            }
-        }
-    }
-
-    /// Propagate the region constraints: this will grow the values
-    /// for each region variable until all the constraints are
-    /// satisfied. Note that some values may grow **too** large to be
-    /// feasible, but we check this later.
-    fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
-        let mut changed = true;
-        let mut dfs = Dfs::new(mir);
-
-        debug!("propagate_constraints()");
-        debug!("propagate_constraints: constraints={:#?}", {
-            let mut constraints: Vec<_> = self.constraints.iter().collect();
-            constraints.sort();
-            constraints
-        });
-
-        // The initial values for each region are derived from the liveness
-        // constraints we have accumulated.
-        let mut inferred_values = self.liveness_constraints.clone();
-
-        while changed {
-            changed = false;
-            debug!("propagate_constraints: --------------------");
-            for constraint in &self.constraints {
-                debug!("propagate_constraints: constraint={:?}", constraint);
-
-                let sub = &inferred_values[constraint.sub].clone();
-                let sup_value = &mut inferred_values[constraint.sup];
-
-                // Grow the value as needed to accommodate the
-                // outlives constraint.
-
-                if dfs.copy(sub, sup_value, constraint.point) {
-                    debug!("propagate_constraints:   sub={:?}", sub);
-                    debug!("propagate_constraints:   sup={:?}", sup_value);
-                    changed = true;
-                }
-            }
-            debug!("\n");
-        }
-
-        self.inferred_values = Some(inferred_values);
-    }
-
-    /// Tries to finds a good span to blame for the fact that `fr1`
-    /// contains `fr2`.
-    fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
-        // Find everything that influenced final value of `fr`.
-        let influenced_fr1 = self.dependencies(fr1);
-
-        // Try to find some outlives constraint `'X: fr2` where `'X`
-        // influenced `fr1`. Blame that.
-        //
-        // NB, this is a pretty bad choice most of the time. In
-        // particular, the connection between `'X` and `fr1` may not
-        // be obvious to the user -- not to mention the naive notion
-        // of dependencies, which doesn't account for the locations of
-        // contraints at all. But it will do for now.
-        for constraint in &self.constraints {
-            if constraint.sub == fr2 && influenced_fr1[constraint.sup] {
-                return constraint.span;
-            }
-        }
-
-        bug!(
-            "could not find any constraint to blame for {:?}: {:?}",
-            fr1,
-            fr2
-        );
-    }
-
-    /// Finds all regions whose values `'a` may depend on in some way.
-    /// Basically if there exists a constraint `'a: 'b @ P`, then `'b`
-    /// and `dependencies('b)` will be in the final set.
-    ///
-    /// Used during error reporting, extremely naive and inefficient.
-    fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, bool> {
-        let mut result_set = IndexVec::from_elem(false, &self.definitions);
-        let mut changed = true;
-        result_set[r0] = true;
-
-        while changed {
-            changed = false;
-            for constraint in &self.constraints {
-                if result_set[constraint.sup] {
-                    if !result_set[constraint.sub] {
-                        result_set[constraint.sub] = true;
-                        changed = true;
-                    }
-                }
-            }
-        }
-
-        result_set
-    }
-}
-
-struct Dfs<'a, 'tcx: 'a> {
-    mir: &'a Mir<'tcx>,
-}
-
-impl<'a, 'tcx> Dfs<'a, 'tcx> {
-    fn new(mir: &'a Mir<'tcx>) -> Self {
-        Self { mir }
-    }
-
-    fn copy(
-        &mut self,
-        from_region: &Region,
-        to_region: &mut Region,
-        start_point: Location,
-    ) -> bool {
-        let mut changed = false;
-
-        let mut stack = vec![];
-        let mut visited = FxHashSet();
-
-        stack.push(start_point);
-        while let Some(p) = stack.pop() {
-            debug!("        dfs: p={:?}", p);
-
-            if !from_region.contains_point(p) {
-                debug!("            not in from-region");
-                continue;
-            }
-
-            if !visited.insert(p) {
-                debug!("            already visited");
-                continue;
-            }
-
-            changed |= to_region.add_point(p);
-
-            let block_data = &self.mir[p.block];
-            let successor_points = if p.statement_index < block_data.statements.len() {
-                vec![
-                    Location {
-                        statement_index: p.statement_index + 1,
-                        ..p
-                    },
-                ]
-            } else {
-                block_data
-                    .terminator()
-                    .successors()
-                    .iter()
-                    .map(|&basic_block| {
-                        Location {
-                            statement_index: 0,
-                            block: basic_block,
-                        }
-                    })
-                    .collect::<Vec<_>>()
-            };
-
-            if successor_points.is_empty() {
-                // If we reach the END point in the graph, then copy
-                // over any skolemized end points in the `from_region`
-                // and make sure they are included in the `to_region`.
-
-                debug!("        dfs: free_regions={:?}", from_region.free_regions);
-                for &fr in &from_region.free_regions {
-                    changed |= to_region.free_regions.insert(fr);
-                }
-            } else {
-                stack.extend(successor_points);
-            }
-        }
-
-        changed
-    }
-}
-
-impl<'tcx> RegionDefinition<'tcx> {
-    fn new(origin: RegionVariableOrigin) -> Self {
-        // Create a new region definition. Note that, for free
-        // regions, these fields get updated later in
-        // `init_free_regions`.
-        Self { origin, name: None }
-    }
-}
-
-impl fmt::Debug for Constraint {
-    fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
-        write!(
-            formatter,
-            "({:?}: {:?} @ {:?}) due to {:?}",
-            self.sup,
-            self.sub,
-            self.point,
-            self.span
-        )
-    }
-}
diff --git a/src/librustc_mir/transform/nll/renumber.rs b/src/librustc_mir/transform/nll/renumber.rs
deleted file mode 100644 (file)
index 1076b77..0000000
+++ /dev/null
@@ -1,165 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc_data_structures::indexed_vec::{Idx, IndexVec};
-use rustc::ty::subst::Substs;
-use rustc::ty::{self, ClosureSubsts, RegionVid, Ty, TypeFoldable};
-use rustc::mir::{BasicBlock, Local, Location, Mir, Statement, StatementKind};
-use rustc::mir::visit::{MutVisitor, TyContext};
-use rustc::infer::{InferCtxt, NLLRegionVariableOrigin};
-
-use super::ToRegionVid;
-use super::free_regions::FreeRegions;
-
-/// Replaces all free regions appearing in the MIR with fresh
-/// inference variables, returning the number of variables created.
-pub fn renumber_mir<'a, 'gcx, 'tcx>(
-    infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-    free_regions: &FreeRegions<'tcx>,
-    mir: &mut Mir<'tcx>,
-) {
-    // Create inference variables for each of the free regions
-    // declared on the function signature.
-    let free_region_inference_vars = (0..free_regions.indices.len())
-        .map(RegionVid::new)
-        .map(|vid_expected| {
-            let r = infcx.next_nll_region_var(NLLRegionVariableOrigin::FreeRegion);
-            assert_eq!(vid_expected, r.to_region_vid());
-            r
-        })
-        .collect();
-
-    debug!("renumber_mir()");
-    debug!("renumber_mir: free_regions={:#?}", free_regions);
-    debug!("renumber_mir: mir.arg_count={:?}", mir.arg_count);
-
-    let mut visitor = NLLVisitor {
-        infcx,
-        free_regions,
-        free_region_inference_vars,
-        arg_count: mir.arg_count,
-    };
-    visitor.visit_mir(mir);
-}
-
-struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
-    infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
-    free_regions: &'a FreeRegions<'tcx>,
-    free_region_inference_vars: IndexVec<RegionVid, ty::Region<'tcx>>,
-    arg_count: usize,
-}
-
-impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
-    /// Replaces all regions appearing in `value` with fresh inference
-    /// variables. This is what we do for almost the entire MIR, with
-    /// the exception of the declared types of our arguments.
-    fn renumber_regions<T>(&mut self, ty_context: TyContext, value: &T) -> T
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        debug!("renumber_regions(value={:?})", value);
-
-        self.infcx
-            .tcx
-            .fold_regions(value, &mut false, |_region, _depth| {
-                let origin = NLLRegionVariableOrigin::Inferred(ty_context);
-                self.infcx.next_nll_region_var(origin)
-            })
-    }
-
-    /// Renumbers the regions appearing in `value`, but those regions
-    /// are expected to be free regions from the function signature.
-    fn renumber_free_regions<T>(&mut self, value: &T) -> T
-    where
-        T: TypeFoldable<'tcx>,
-    {
-        debug!("renumber_free_regions(value={:?})", value);
-
-        self.infcx
-            .tcx
-            .fold_regions(value, &mut false, |region, _depth| {
-                let index = self.free_regions.indices[&region];
-                self.free_region_inference_vars[index]
-            })
-    }
-
-    fn is_argument_or_return_slot(&self, local: Local) -> bool {
-        // The first argument is return slot, next N are arguments.
-        local.index() <= self.arg_count
-    }
-}
-
-impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
-    fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
-        let is_arg = match ty_context {
-            TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
-            TyContext::ReturnTy(..) => true,
-            TyContext::Location(..) => false,
-        };
-        debug!(
-            "visit_ty(ty={:?}, is_arg={:?}, ty_context={:?})",
-            ty,
-            is_arg,
-            ty_context
-        );
-
-        let old_ty = *ty;
-        *ty = if is_arg {
-            self.renumber_free_regions(&old_ty)
-        } else {
-            self.renumber_regions(ty_context, &old_ty)
-        };
-        debug!("visit_ty: ty={:?}", ty);
-    }
-
-    fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
-        debug!("visit_substs(substs={:?}, location={:?})", substs, location);
-
-        let ty_context = TyContext::Location(location);
-        *substs = self.renumber_regions(ty_context, &{ *substs });
-
-        debug!("visit_substs: substs={:?}", substs);
-    }
-
-    fn visit_region(&mut self, region: &mut ty::Region<'tcx>, location: Location) {
-        debug!("visit_region(region={:?}, location={:?})", region, location);
-
-        let old_region = *region;
-        let ty_context = TyContext::Location(location);
-        *region = self.renumber_regions(ty_context, &old_region);
-
-        debug!("visit_region: region={:?}", region);
-    }
-
-    fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
-        debug!(
-            "visit_closure_substs(substs={:?}, location={:?})",
-            substs,
-            location
-        );
-
-        let ty_context = TyContext::Location(location);
-        *substs = self.renumber_regions(ty_context, substs);
-
-        debug!("visit_closure_substs: substs={:?}", substs);
-    }
-
-    fn visit_statement(
-        &mut self,
-        block: BasicBlock,
-        statement: &mut Statement<'tcx>,
-        location: Location,
-    ) {
-        if let StatementKind::EndRegion(_) = statement.kind {
-            statement.kind = StatementKind::Nop;
-        }
-        self.super_statement(block, statement, location);
-    }
-}
diff --git a/src/librustc_mir/transform/nll/subtype_constraint_generation.rs b/src/librustc_mir/transform/nll/subtype_constraint_generation.rs
deleted file mode 100644 (file)
index c1850c7..0000000
+++ /dev/null
@@ -1,112 +0,0 @@
-// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
-// file at the top-level directory of this distribution and at
-// http://rust-lang.org/COPYRIGHT.
-//
-// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
-// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
-// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
-// option. This file may not be copied, modified, or distributed
-// except according to those terms.
-
-use rustc::mir::Mir;
-use rustc::infer::region_constraints::Constraint;
-use rustc::infer::region_constraints::RegionConstraintData;
-use rustc::ty;
-use transform::type_check::MirTypeckRegionConstraints;
-use transform::type_check::OutlivesSet;
-
-use super::free_regions::FreeRegions;
-use super::region_infer::RegionInferenceContext;
-
-/// When the MIR type-checker executes, it validates all the types in
-/// the MIR, and in the process generates a set of constraints that
-/// must hold regarding the regions in the MIR, along with locations
-/// *where* they must hold. This code takes those constriants and adds
-/// them into the NLL `RegionInferenceContext`.
-pub(super) fn generate<'tcx>(
-    regioncx: &mut RegionInferenceContext<'tcx>,
-    free_regions: &FreeRegions<'tcx>,
-    mir: &Mir<'tcx>,
-    constraints: &MirTypeckRegionConstraints<'tcx>,
-) {
-    SubtypeConstraintGenerator {
-        regioncx,
-        free_regions,
-        mir,
-    }.generate(constraints);
-}
-
-struct SubtypeConstraintGenerator<'cx, 'tcx: 'cx> {
-    regioncx: &'cx mut RegionInferenceContext<'tcx>,
-    free_regions: &'cx FreeRegions<'tcx>,
-    mir: &'cx Mir<'tcx>,
-}
-
-impl<'cx, 'tcx> SubtypeConstraintGenerator<'cx, 'tcx> {
-    fn generate(&mut self, constraints: &MirTypeckRegionConstraints<'tcx>) {
-        let MirTypeckRegionConstraints {
-            liveness_set,
-            outlives_sets,
-        } = constraints;
-
-        debug!(
-            "generate(liveness_set={} items, outlives_sets={} items)",
-            liveness_set.len(),
-            outlives_sets.len()
-        );
-
-        for (region, location) in liveness_set {
-            debug!("generate: {:#?} is live at {:#?}", region, location);
-            let region_vid = self.to_region_vid(region);
-            self.regioncx.add_live_point(region_vid, *location);
-        }
-
-        for OutlivesSet { locations, data } in outlives_sets {
-            debug!("generate: constraints at: {:#?}", locations);
-            let RegionConstraintData {
-                constraints,
-                verifys,
-                givens,
-            } = data;
-
-            for constraint in constraints.keys() {
-                debug!("generate: constraint: {:?}", constraint);
-                let (a_vid, b_vid) = match constraint {
-                    Constraint::VarSubVar(a_vid, b_vid) => (*a_vid, *b_vid),
-                    Constraint::RegSubVar(a_r, b_vid) => (self.to_region_vid(a_r), *b_vid),
-                    Constraint::VarSubReg(a_vid, b_r) => (*a_vid, self.to_region_vid(b_r)),
-                    Constraint::RegSubReg(a_r, b_r) => {
-                        (self.to_region_vid(a_r), self.to_region_vid(b_r))
-                    }
-                };
-
-                // We have the constraint that `a_vid <= b_vid`. Add
-                // `b_vid: a_vid` to our region checker. Note that we
-                // reverse direction, because `regioncx` talks about
-                // "outlives" (`>=`) whereas the region constraints
-                // talk about `<=`.
-                let span = self.mir.source_info(locations.from_location).span;
-                self.regioncx
-                    .add_outlives(span, b_vid, a_vid, locations.at_location);
-            }
-
-            assert!(verifys.is_empty(), "verifys not yet implemented");
-            assert!(
-                givens.is_empty(),
-                "MIR type-checker does not use givens (thank goodness)"
-            );
-        }
-    }
-
-    fn to_region_vid(&self, r: ty::Region<'tcx>) -> ty::RegionVid {
-        // Every region that we see in the constraints came from the
-        // MIR or from the parameter environment. If the former, it
-        // will be a region variable.  If the latter, it will be in
-        // the set of free regions *somewhere*.
-        if let ty::ReVar(vid) = r {
-            *vid
-        } else {
-            self.free_regions.indices[&r]
-        }
-    }
-}
diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.rs
new file mode 100644 (file)
index 0000000..0047f6d
--- /dev/null
@@ -0,0 +1,33 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+struct Foo<'p> { a: String, b: Wrap<'p> }
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    let s = String::from("str");
+    let foo = Foo { a: s, b: wrap };
+    std::mem::drop(foo.b);
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+    // FIXME ^ Should not error in the future with implicit dtors, only manually implemented ones
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr b/src/test/ui/nll/maybe-initialized-drop-implicit-fragment-drop.stderr
new file mode 100644 (file)
index 0000000..389334f
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:31:5
+   |
+27 |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+...
+31 |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs b/src/test/ui/nll/maybe-initialized-drop-uninitialized.rs
new file mode 100644 (file)
index 0000000..64a4d39
--- /dev/null
@@ -0,0 +1,28 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    std::mem::drop(wrap);
+    x = 1; // OK, drop is inert
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr b/src/test/ui/nll/maybe-initialized-drop-uninitialized.stderr
new file mode 100644 (file)
index 0000000..e69de29
diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs b/src/test/ui/nll/maybe-initialized-drop-with-fragment.rs
new file mode 100644 (file)
index 0000000..3242136
--- /dev/null
@@ -0,0 +1,32 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+struct Foo<'p> { a: String, b: Wrap<'p> }
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    let s = String::from("str");
+    let foo = Foo { a: s, b: wrap };
+    std::mem::drop(foo.a);
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr b/src/test/ui/nll/maybe-initialized-drop-with-fragment.stderr
new file mode 100644 (file)
index 0000000..9edeca2
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/maybe-initialized-drop-with-fragment.rs:31:5
+   |
+27 |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+...
+31 |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.rs
new file mode 100644 (file)
index 0000000..3e32818
--- /dev/null
@@ -0,0 +1,34 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+struct Foo<'p> { a: String, b: Wrap<'p> }
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    let s = String::from("str");
+    let foo = Foo { a: s, b: wrap };
+    std::mem::drop(foo.a);
+    std::mem::drop(foo.b);
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+    // FIXME ^ This currently errors and it should not.
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr b/src/test/ui/nll/maybe-initialized-drop-with-uninitialized-fragments.stderr
new file mode 100644 (file)
index 0000000..24d0d6d
--- /dev/null
@@ -0,0 +1,11 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/maybe-initialized-drop-with-uninitialized-fragments.rs:32:5
+   |
+27 |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+...
+32 |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+
diff --git a/src/test/ui/nll/maybe-initialized-drop.rs b/src/test/ui/nll/maybe-initialized-drop.rs
new file mode 100644 (file)
index 0000000..291fcbd
--- /dev/null
@@ -0,0 +1,27 @@
+// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+//compile-flags: -Z emit-end-regions -Zborrowck=mir -Z nll
+
+#![allow(warnings)]
+
+struct Wrap<'p> { p: &'p mut i32 }
+
+impl<'p> Drop for Wrap<'p> {
+    fn drop(&mut self) {
+        *self.p += 1;
+    }
+}
+
+fn main() {
+    let mut x = 0;
+    let wrap = Wrap { p: &mut x };
+    x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+}
diff --git a/src/test/ui/nll/maybe-initialized-drop.stderr b/src/test/ui/nll/maybe-initialized-drop.stderr
new file mode 100644 (file)
index 0000000..7b1b55d
--- /dev/null
@@ -0,0 +1,10 @@
+error[E0506]: cannot assign to `x` because it is borrowed
+  --> $DIR/maybe-initialized-drop.rs:26:5
+   |
+25 |     let wrap = Wrap { p: &mut x };
+   |                          ------ borrow of `x` occurs here
+26 |     x = 1; //~ ERROR cannot assign to `x` because it is borrowed [E0506]
+   |     ^^^^^ assignment to borrowed `x` occurs here
+
+error: aborting due to previous error
+