Skip to content

Commit

Permalink
MIR based borrow check (opt-in).
Browse files Browse the repository at this point in the history
One can either use `-Z borrowck-mir` or add the `#[rustc_mir_borrowck]` attribute
to opt into MIR based borrow checking.

Note that regardless of whether one opts in or not, AST-based borrow
check will still run as well.  The errors emitted from AST-based
borrow check will include a "(Ast)" suffix in their error message,
while the errors emitted from MIR-based borrow check will include a
"(Mir)" suffix.

post-rebase: removed check for intra-statement mutual conflict;
replaced with assertion checking that at most one borrow is generated
per statement.

post-rebase: removed dead code: `IdxSet::pairs` and supporting stuff.
  • Loading branch information
pnkfelix committed Aug 16, 2017
1 parent 869f05a commit 018784a
Show file tree
Hide file tree
Showing 7 changed files with 1,422 additions and 4 deletions.
2 changes: 2 additions & 0 deletions src/librustc/session/config.rs
Expand Up @@ -918,6 +918,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options,
"when debug-printing compiler state, do not include spans"), // o/w tests have closure@path
identify_regions: bool = (false, parse_bool, [UNTRACKED],
"make unnamed regions display as '# (where # is some non-ident unique id)"),
borrowck_mir: bool = (false, parse_bool, [UNTRACKED],
"implicitly treat functions as if they have `#[rustc_mir_borrowck]` attribute"),
time_passes: bool = (false, parse_bool, [UNTRACKED],
"measure time of each rustc pass"),
count_llvm_insns: bool = (false, parse_bool,
Expand Down
30 changes: 30 additions & 0 deletions src/librustc_data_structures/indexed_set.rs
Expand Up @@ -159,6 +159,36 @@ impl<T: Idx> IdxSet<T> {
pub fn each_bit<F>(&self, max_bits: usize, f: F) where F: FnMut(T) {
each_bit(self, max_bits, f)
}

/// Removes all elements from this set.
pub fn reset_to_empty(&mut self) {
for word in self.words_mut() { *word = 0; }
}

pub fn elems(&self, universe_size: usize) -> Elems<T> {
Elems { i: 0, set: self, universe_size: universe_size }
}
}

pub struct Elems<'a, T: Idx> { i: usize, set: &'a IdxSet<T>, universe_size: usize }

impl<'a, T: Idx> Iterator for Elems<'a, T> {
type Item = T;
fn next(&mut self) -> Option<T> {
if self.i >= self.universe_size { return None; }
let mut i = self.i;
loop {
if i >= self.universe_size {
self.i = i; // (mark iteration as complete.)
return None;
}
if self.set.contains(&T::new(i)) {
self.i = i + 1; // (next element to start at.)
return Some(T::new(i));
}
i = i + 1;
}
}
}

fn each_bit<T: Idx, F>(words: &IdxSet<T>, max_bits: usize, mut f: F) where F: FnMut(T) {
Expand Down
1 change: 1 addition & 0 deletions src/librustc_driver/driver.rs
Expand Up @@ -970,6 +970,7 @@ pub fn phase_3_run_analysis_passes<'tcx, F, R>(sess: &'tcx Session,
// We compute "constant qualifications" between MIR_CONST and MIR_VALIDATED.

// What we need to run borrowck etc.
passes.push_pass(MIR_CONST, mir::transform::borrow_check::BorrowckMir);
passes.push_pass(MIR_VALIDATED, mir::transform::qualify_consts::QualifyAndPromoteConstants);
passes.push_pass(MIR_VALIDATED,
mir::transform::simplify_branches::SimplifyBranches::new("initial"));
Expand Down
116 changes: 112 additions & 4 deletions src/librustc_mir/dataflow/mod.rs
Expand Up @@ -15,7 +15,7 @@ use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::bitslice::{bitwise, BitwiseOperator};

use rustc::ty::{self, TyCtxt};
use rustc::mir::{self, Mir, BasicBlock, Location};
use rustc::mir::{self, Mir, BasicBlock, BasicBlockData, Location, Statement, Terminator};
use rustc::session::Session;

use std::fmt::{self, Debug};
Expand Down Expand Up @@ -47,7 +47,19 @@ pub(crate) struct DataflowBuilder<'a, 'tcx: 'a, BD> where BD: BitDenotation
}

pub trait Dataflow<BD: BitDenotation> {
fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug;
/// Sets up and runs the dataflow problem, using `p` to render results if
/// implementation so chooses.
fn dataflow<P>(&mut self, p: P) where P: Fn(&BD, BD::Idx) -> &Debug {
let _ = p; // default implementation does not instrument process.
self.build_sets();
self.propagate();
}

/// Sets up the entry, gen, and kill sets for this instance of a dataflow problem.
fn build_sets(&mut self);

/// Finds a fixed-point solution to this instance of a dataflow problem.
fn propagate(&mut self);
}

impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD>
Expand All @@ -59,6 +71,9 @@ impl<'a, 'tcx: 'a, BD> Dataflow<BD> for DataflowBuilder<'a, 'tcx, BD>
self.flow_state.propagate();
self.post_dataflow_instrumentation(|c,i| p(c,i)).unwrap();
}

fn build_sets(&mut self) { self.flow_state.build_sets(); }
fn propagate(&mut self) { self.flow_state.propagate(); }
}

pub(crate) fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option<MetaItem> {
Expand Down Expand Up @@ -254,6 +269,93 @@ impl<E:Idx> Bits<E> {
}
}

/// DataflowResultsConsumer abstracts over walking the MIR with some
/// already constructed dataflow results.
///
/// It abstracts over the FlowState and also completely hides the
/// underlying flow analysis results, because it needs to handle cases
/// where we are combining the results of *multiple* flow analyses
/// (e.g. borrows + inits + uninits).
pub trait DataflowResultsConsumer<'a, 'tcx: 'a> {
type FlowState;

// Observation Hooks: override (at least one of) these to get analysis feedback.
fn visit_block_entry(&mut self,
_bb: BasicBlock,
_flow_state: &Self::FlowState) {}

fn visit_statement_entry(&mut self,
_loc: Location,
_stmt: &Statement<'tcx>,
_flow_state: &Self::FlowState) {}

fn visit_terminator_entry(&mut self,
_loc: Location,
_term: &Terminator<'tcx>,
_flow_state: &Self::FlowState) {}

// Main entry point: this drives the processing of results.

fn analyze_results(&mut self, flow_uninit: &mut Self::FlowState) {
let flow = flow_uninit;
for bb in self.mir().basic_blocks().indices() {
self.reset_to_entry_of(bb, flow);
self.process_basic_block(bb, flow);
}
}

fn process_basic_block(&mut self, bb: BasicBlock, flow_state: &mut Self::FlowState) {
let BasicBlockData { ref statements, ref terminator, is_cleanup: _ } =
self.mir()[bb];
let mut location = Location { block: bb, statement_index: 0 };
for stmt in statements.iter() {
self.reconstruct_statement_effect(location, flow_state);
self.visit_statement_entry(location, stmt, flow_state);
self.apply_local_effect(location, flow_state);
location.statement_index += 1;
}

if let Some(ref term) = *terminator {
self.reconstruct_terminator_effect(location, flow_state);
self.visit_terminator_entry(location, term, flow_state);

// We don't need to apply the effect of the terminator,
// since we are only visiting dataflow state on control
// flow entry to the various nodes. (But we still need to
// reconstruct the effect, because the visit method might
// inspect it.)
}
}

// Delegated Hooks: Provide access to the MIR and process the flow state.

fn mir(&self) -> &'a Mir<'tcx>;

// reset the state bitvector to represent the entry to block `bb`.
fn reset_to_entry_of(&mut self,
bb: BasicBlock,
flow_state: &mut Self::FlowState);

// build gen + kill sets for statement at `loc`.
fn reconstruct_statement_effect(&mut self,
loc: Location,
flow_state: &mut Self::FlowState);

// build gen + kill sets for terminator for `loc`.
fn reconstruct_terminator_effect(&mut self,
loc: Location,
flow_state: &mut Self::FlowState);

// apply current gen + kill sets to `flow_state`.
//
// (`bb` and `stmt_idx` parameters can be ignored if desired by
// client. For the terminator, the `stmt_idx` will be the number
// of statements in the block.)
fn apply_local_effect(&mut self,
loc: Location,
flow_state: &mut Self::FlowState);
}

pub struct DataflowAnalysis<'a, 'tcx: 'a, O>
where O: BitDenotation
{
Expand All @@ -269,6 +371,8 @@ impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O>
DataflowResults(self.flow_state)
}

pub fn flow_state(&self) -> &DataflowState<O> { &self.flow_state }

pub fn mir(&self) -> &'a Mir<'tcx> { self.mir }
}

Expand All @@ -278,10 +382,14 @@ impl<O: BitDenotation> DataflowResults<O> {
pub fn sets(&self) -> &AllSets<O::Idx> {
&self.0.sets
}

pub fn operator(&self) -> &O {
&self.0.operator
}
}

// FIXME: This type shouldn't be public, but the graphviz::MirWithFlowState trait
// references it in a method signature. Look into using `pub(crate)` to address this.
/// State of a dataflow analysis; couples a collection of bit sets
/// with operator used to initialize and merge bits during analysis.
pub struct DataflowState<O: BitDenotation>
{
/// All the sets for the analysis. (Factored into its
Expand Down
6 changes: 6 additions & 0 deletions src/librustc_mir/dataflow/move_paths/mod.rs
Expand Up @@ -108,6 +108,12 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> {
}
}

impl<'tcx> fmt::Display for MovePath<'tcx> {
fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
write!(w, "{:?}", self.lvalue)
}
}

#[derive(Debug)]
pub struct MoveData<'tcx> {
pub move_paths: IndexVec<MovePathIndex, MovePath<'tcx>>,
Expand Down

0 comments on commit 018784a

Please sign in to comment.