MIR based borrow check (opt-in).

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.
This commit is contained in:
Felix S. Klock II 2017-07-05 14:52:18 +02:00
parent 869f05a521
commit 018784afc9
7 changed files with 1422 additions and 4 deletions

View File

@ -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,

View File

@ -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) {

View File

@ -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"));

View File

@ -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};
@ -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>
@ -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> {
@ -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
{
@ -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 }
}
@ -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

View File

@ -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>>,

File diff suppressed because it is too large Load Diff

View File

@ -31,6 +31,7 @@ pub mod simplify;
pub mod erase_regions;
pub mod no_landing_pads;
pub mod type_check;
pub mod borrow_check;
pub mod rustc_peek;
pub mod elaborate_drops;
pub mod add_call_guards;