diff --git a/src/librustc_mir/borrow_check/location.rs b/src/librustc_mir/borrow_check/location.rs new file mode 100644 index 000000000000..e09f0ee9e2a2 --- /dev/null +++ b/src/librustc_mir/borrow_check/location.rs @@ -0,0 +1,126 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // TODO -- will be used in a later commit, remove then + +use rustc::mir::{BasicBlock, Location, Mir}; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; + +/// Maps between a MIR Location, which identifies the a particular +/// statement within a basic block, to a "rich location", which +/// identifies at a finer granularity. In particular, we distinguish +/// the *start* of a statement and the *mid-point*. The mid-point is +/// the point *just* before the statement takes effect; in particular, +/// for an assignment `A = B`, it is the point where B is about to be +/// written into A. This mid-point is a kind of hack to work around +/// our inability to track the position information at sufficient +/// granularity through outlives relations; however, the rich location +/// table serves another purpose: it compresses locations from +/// multiple words into a single u32. +crate struct LocationTable { + num_points: usize, + statements_before_block: IndexVec, +} + +newtype_index!(LocationIndex { DEBUG_FORMAT = "LocationIndex({})" }); + +#[derive(Copy, Clone, Debug)] +crate enum RichLocation { + Start(Location), + Mid(Location), +} + +impl LocationTable { + crate fn new(mir: &Mir<'_>) -> Self { + let mut num_points = 0; + let statements_before_block = mir.basic_blocks() + .iter() + .map(|block_data| { + let v = num_points; + num_points += (block_data.statements.len() + 1) * 2; + v + }) + .collect(); + + debug!( + "LocationTable(statements_before_block={:#?})", + statements_before_block + ); + debug!("LocationTable: num_points={:#?}", num_points); + + Self { + num_points, + statements_before_block, + } + } + + #[allow(dead_code)] // TODO + crate fn all_points(&self) -> impl Iterator { + (0..self.num_points).map(LocationIndex::new) + } + + crate fn start_index(&self, location: Location) -> LocationIndex { + let Location { + block, + statement_index, + } = location; + let start_index = self.statements_before_block[block]; + LocationIndex::new(start_index + statement_index * 2) + } + + crate fn mid_index(&self, location: Location) -> LocationIndex { + let Location { + block, + statement_index, + } = location; + let start_index = self.statements_before_block[block]; + LocationIndex::new(start_index + statement_index * 2 + 1) + } + + crate fn to_location(&self, index: LocationIndex) -> RichLocation { + let point_index = index.index(); + + // Find the basic block. We have a vector with the + // starting index of the statement in each block. Imagine + // we have statement #22, and we have a vector like: + // + // [0, 10, 20] + // + // In that case, this represents point_index 2 of + // basic block BB2. We know this because BB0 accounts for + // 0..10, BB1 accounts for 11..20, and BB2 accounts for + // 20... + // + // To compute this, we could do a binary search, but + // because I am lazy we instead iterate through to find + // the last point where the "first index" (0, 10, or 20) + // was less than the statement index (22). In our case, this will + // be (BB2, 20). + let (block, &first_index) = self.statements_before_block + .iter_enumerated() + .filter(|(_, first_index)| **first_index <= point_index) + .last() + .unwrap(); + + let statement_index = (point_index - first_index) / 2; + if index.is_start() { + RichLocation::Start(Location { block, statement_index }) + } else { + RichLocation::Mid(Location { block, statement_index }) + } + } +} + +impl LocationIndex { + fn is_start(&self) -> bool { + // even indices are start points; odd indices are mid points + (self.index() % 2) == 0 + } +} diff --git a/src/librustc_mir/borrow_check/mod.rs b/src/librustc_mir/borrow_check/mod.rs index c619f350f58d..3e3f510e308c 100644 --- a/src/librustc_mir/borrow_check/mod.rs +++ b/src/librustc_mir/borrow_check/mod.rs @@ -50,12 +50,14 @@ use std::iter; use self::borrow_set::{BorrowSet, BorrowData}; use self::flows::Flows; +use self::location::LocationTable; use self::prefixes::PrefixSet; use self::MutateMode::{JustWrite, WriteAndRead}; crate mod borrow_set; mod error_reporting; mod flows; +mod location; crate mod place_ext; mod prefixes; @@ -110,6 +112,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( let mut mir: Mir<'tcx> = input_mir.clone(); let free_regions = nll::replace_regions_in_mir(infcx, def_id, param_env, &mut mir); let mir = &mir; // no further changes + let location_table = &LocationTable::new(mir); let move_data: MoveData<'tcx> = match MoveData::gather_moves(mir, tcx) { Ok(move_data) => move_data, @@ -199,6 +202,7 @@ fn do_mir_borrowck<'a, 'gcx, 'tcx>( def_id, free_regions, mir, + location_table, param_env, &mut flow_inits, &mdpe.move_data, diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 411bf5b55ed6..80ad3ecb9f85 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -9,6 +9,7 @@ // except according to those terms. use borrow_check::borrow_set::BorrowSet; +use borrow_check::location::LocationTable; use rustc::hir::def_id::DefId; use rustc::mir::{ClosureRegionRequirements, ClosureOutlivesSubject, Mir}; use rustc::infer::InferCtxt; @@ -70,6 +71,7 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>( def_id: DefId, universal_regions: UniversalRegions<'tcx>, mir: &Mir<'tcx>, + _location_table: &LocationTable, param_env: ty::ParamEnv<'gcx>, flow_inits: &mut FlowAtLocation>, move_data: &MoveData<'tcx>,