From cd81b60a4aa45f0eb72c5170673f24dc1a1c3592 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 15:12:52 +0200 Subject: [PATCH 01/48] fix bug in `debug!` output from `rustc::middle::dataflow` (bug was cut/pasted into `rustc_borrowck::bitslice`, so I fixed it there as well.) --- src/librustc/middle/dataflow.rs | 4 ++-- src/librustc_borrowck/bitslice.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc/middle/dataflow.rs b/src/librustc/middle/dataflow.rs index 41b27a48b29f8..4d01b59001c5d 100644 --- a/src/librustc/middle/dataflow.rs +++ b/src/librustc/middle/dataflow.rs @@ -660,8 +660,8 @@ fn set_bit(words: &mut [usize], bit: usize) -> bool { } fn bit_str(bit: usize) -> String { - let byte = bit >> 8; - let lobits = 1 << (bit & 0xFF); + let byte = bit >> 3; + let lobits = 1 << (bit & 0b111); format!("[{}:{}-{:02x}]", bit, byte, lobits) } diff --git a/src/librustc_borrowck/bitslice.rs b/src/librustc_borrowck/bitslice.rs index a4aa7ae15744d..0454b0f3e7cc7 100644 --- a/src/librustc_borrowck/bitslice.rs +++ b/src/librustc_borrowck/bitslice.rs @@ -73,8 +73,8 @@ fn bit_lookup(bit: usize) -> BitLookup { fn bit_str(bit: usize) -> String { - let byte = bit >> 8; - let lobits = 1 << (bit & 0xFF); + let byte = bit >> 3; + let lobits = 1 << (bit & 0b111); format!("[{}:{}-{:02x}]", bit, byte, lobits) } From bfe789c04412fd8bfb33d1d66f7f8c233a349345 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 15:57:49 +0200 Subject: [PATCH 02/48] fixes to `librustc_borrowck::bitslice::bits_to_string`, used for graphviz printing. --- src/librustc_borrowck/bitslice.rs | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/src/librustc_borrowck/bitslice.rs b/src/librustc_borrowck/bitslice.rs index 0454b0f3e7cc7..ca672e808843b 100644 --- a/src/librustc_borrowck/bitslice.rs +++ b/src/librustc_borrowck/bitslice.rs @@ -78,25 +78,29 @@ fn bit_str(bit: usize) -> String { format!("[{}:{}-{:02x}]", bit, byte, lobits) } -pub fn bits_to_string(words: &[usize], bytes: usize) -> String { +pub fn bits_to_string(words: &[usize], bits: usize) -> String { let mut result = String::new(); let mut sep = '['; // Note: this is a little endian printout of bytes. + // i tracks how many bits we have printed so far. let mut i = 0; for &word in words.iter() { let mut v = word; - for _ in 0..mem::size_of::() { - let byte = v & 0xFF; - if i >= bytes { - assert!(byte == 0); - } else { - result.push(sep); - result.push_str(&format!("{:02x}", byte)); - } + loop { // for each byte in `v`: + let remain = bits - i; + // If less than a byte remains, then mask just that many bits. + let mask = if remain <= 8 { (1 << remain) - 1 } else { 0xFF }; + assert!(mask <= 0xFF); + let byte = v & mask; + + result.push(sep); + result.push_str(&format!("{:02x}", byte)); + + if remain <= 8 { break; } v >>= 8; - i += 1; + i += 8; sep = '-'; } } From 306ca4ca4fcad9b66d375cdd3329093aa7447575 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 16:06:18 +0200 Subject: [PATCH 03/48] `rustc_mir::pretty`: factor out scope entry/exit annotation computation. --- src/librustc_mir/pretty.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index fb29cbd5fa8a2..f11d0c84ec29d 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -106,12 +106,9 @@ enum Annotation { ExitScope(ScopeId), } -pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - src: MirSource, - mir: &Mir<'tcx>, - w: &mut Write, - auxiliary: Option<&ScopeAuxiliaryVec>) - -> io::Result<()> { +fn scope_entry_exit_annotations(auxiliary: Option<&ScopeAuxiliaryVec>) + -> FnvHashMap> +{ // compute scope/entry exit annotations let mut annotations = FnvHashMap(); if let Some(auxiliary) = auxiliary { @@ -129,7 +126,16 @@ pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } } + return annotations; +} +pub fn write_mir_fn<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + src: MirSource, + mir: &Mir<'tcx>, + w: &mut Write, + auxiliary: Option<&ScopeAuxiliaryVec>) + -> io::Result<()> { + let annotations = scope_entry_exit_annotations(auxiliary); write_mir_intro(tcx, src, mir, w)?; for block in mir.all_basic_blocks() { write_basic_block(tcx, block, mir, w, &annotations)?; From 90b7a86268036947f0b11a465728ea88a2fb1480 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 16:13:44 +0200 Subject: [PATCH 04/48] `rustc_mir::pretty` refactoring: break `fn write_fn_intro` into two routines. (The crucial thing these changes are working toward (but are not yet in this commit) is a way to pretty-print MIR without having the `NodeId` for that MIR in hand.) --- src/librustc_mir/pretty.rs | 20 +++++++++++++++----- 1 file changed, 15 insertions(+), 5 deletions(-) diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index f11d0c84ec29d..a6bbd55ffa7a4 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -276,6 +276,14 @@ fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir, w: &mut Write) -> io::Result<()> { + write_mir_sig(tcx, src, mir, w)?; + writeln!(w, " {{")?; + write_mir_decls(tcx, mir, w) +} + +fn write_mir_sig(tcx: TyCtxt, src: MirSource, mir: &Mir, w: &mut Write) + -> io::Result<()> +{ match src { MirSource::Fn(_) => write!(w, "fn")?, MirSource::Const(_) => write!(w, "const")?, @@ -301,16 +309,18 @@ fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // fn return type. match mir.return_ty { - ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty)?, - ty::FnOutput::FnDiverging => write!(w, "!")?, + ty::FnOutput::FnConverging(ty) => write!(w, "{}", ty), + ty::FnOutput::FnDiverging => write!(w, "!"), } } else { assert!(mir.arg_decls.is_empty()); - write!(w, ": {} =", mir.return_ty.unwrap())?; + write!(w, ": {} =", mir.return_ty.unwrap()) } +} - writeln!(w, " {{")?; - +fn write_mir_decls(tcx: TyCtxt, mir: &Mir, w: &mut Write) + -> io::Result<()> +{ // User variable types (including the user's name in a comment). for (i, var) in mir.var_decls.iter().enumerate() { let mut_str = if var.mutability == Mutability::Mut { From 4446e793dad3bf28c67f1bc70d6380f13de63b49 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 16:22:27 +0200 Subject: [PATCH 05/48] Expose pretty print routines that accept just `mir` (no need for a `NodeId`). --- src/librustc_mir/pretty.rs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index a6bbd55ffa7a4..33f1df17bcba3 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -269,6 +269,29 @@ fn write_scope_tree(tcx: TyCtxt, Ok(()) } +pub fn write_mir_named(tcx: &ty::TyCtxt, name: &str, mir: &Mir, w: &mut Write, auxiliary: Option<&ScopeAuxiliaryVec>) +-> io::Result<()> { + + let annotations = scope_entry_exit_annotations(auxiliary); + write_mir_intro_named(tcx, name, mir, w)?; + for block in mir.all_basic_blocks() { + write_basic_block(tcx, block, mir, w, &annotations)?; + } + writeln!(w, "}}") +} + +/// Write out a human-readable textual representation of the MIR's +/// `fn` type and the types of its local variables (both user-defined +/// bindings and compiler temporaries). Assumes the function +/// represented by `mir` is named `name`. Note: Generated output +/// introduces an open curly that needs to be closed. +pub fn write_mir_intro_named(tcx: &ty::TyCtxt, name: &str, mir: &Mir, w: &mut Write) +-> io::Result<()> { + write_mir_fn_sig(tcx, name, mir, w)?; + writeln!(w, " {{")?; + write_mir_fn_decls(tcx, mir, w) +} + /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, From 129b371caea8a5eab4579f78cd07deb2e95445b0 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 17:27:17 +0200 Subject: [PATCH 06/48] One-line doc clarification for representation of unit type `()`. --- src/librustc_mir/pretty.rs | 23 ----------------------- 1 file changed, 23 deletions(-) diff --git a/src/librustc_mir/pretty.rs b/src/librustc_mir/pretty.rs index 33f1df17bcba3..a6bbd55ffa7a4 100644 --- a/src/librustc_mir/pretty.rs +++ b/src/librustc_mir/pretty.rs @@ -269,29 +269,6 @@ fn write_scope_tree(tcx: TyCtxt, Ok(()) } -pub fn write_mir_named(tcx: &ty::TyCtxt, name: &str, mir: &Mir, w: &mut Write, auxiliary: Option<&ScopeAuxiliaryVec>) --> io::Result<()> { - - let annotations = scope_entry_exit_annotations(auxiliary); - write_mir_intro_named(tcx, name, mir, w)?; - for block in mir.all_basic_blocks() { - write_basic_block(tcx, block, mir, w, &annotations)?; - } - writeln!(w, "}}") -} - -/// Write out a human-readable textual representation of the MIR's -/// `fn` type and the types of its local variables (both user-defined -/// bindings and compiler temporaries). Assumes the function -/// represented by `mir` is named `name`. Note: Generated output -/// introduces an open curly that needs to be closed. -pub fn write_mir_intro_named(tcx: &ty::TyCtxt, name: &str, mir: &Mir, w: &mut Write) --> io::Result<()> { - write_mir_fn_sig(tcx, name, mir, w)?; - writeln!(w, " {{")?; - write_mir_fn_decls(tcx, mir, w) -} - /// Write out a human-readable textual representation of the MIR's `fn` type and the types of its /// local variables (both user-defined bindings and compiler temporaries). fn write_mir_intro<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, From b4972b00c9f729bec88611e27cc97728403724ac Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 18:00:33 +0200 Subject: [PATCH 07/48] Add helper method for getting the dataflow results at exit from a basic block. --- src/librustc_borrowck/borrowck/mir/dataflow.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs index d6dd176e3ba28..f0fb0f1515d3d 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow.rs @@ -303,6 +303,14 @@ impl AllSets { pub fn on_entry_set_for(&self, block_idx: usize) -> &[usize] { self.lookup_set_for(&self.on_entry_sets, block_idx) } + pub fn on_exit_set_for(&self, block_idx: usize) -> Vec { + let mut set: Vec<_> = self.on_entry_set_for(block_idx).iter() + .map(|x|*x) + .collect(); + bitwise(&mut set[..], self.gen_set_for(block_idx), &Union); + bitwise(&mut set[..], self.kill_set_for(block_idx), &Subtract); + return set; + } } impl DataflowState { From 79ab85544bc14a3eadd8ad2dfba24fd5757a6d23 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 18:05:28 +0200 Subject: [PATCH 08/48] Remove `&self` parameter from `DataflowOperator::initial_value`. --- src/librustc_borrowck/borrowck/mir/dataflow.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs index f0fb0f1515d3d..fcdb0c4bf5408 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow.rs @@ -159,7 +159,7 @@ impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn> PropagationContext<'c, 'b, 'a, 'tcx where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue) { fn reset(&mut self, bits: &mut [usize]) { - let e = if self.mbcx.flow_state.operator.initial_value() {usize::MAX} else {0}; + let e = if MoveData::initial_value() {usize::MAX} else {0}; for b in bits { *b = e; } @@ -361,7 +361,7 @@ pub trait BitwiseOperator { /// Parameterization for the precise form of data flow that is used. pub trait DataflowOperator : BitwiseOperator { /// Specifies the initial value for each bit in the `on_entry` set - fn initial_value(&self) -> bool; + fn initial_value() -> bool; } pub trait BitDenotation: DataflowOperator { @@ -382,7 +382,7 @@ impl DataflowState { let num_blocks = mir.basic_blocks.len(); let num_words = num_blocks * words_per_block; - let entry = if denotation.initial_value() { usize::MAX } else {0}; + let entry = if D::initial_value() { usize::MAX } else {0}; let zeroes = Bits::new(0, num_words); let on_entry = Bits::new(entry, num_words); @@ -482,7 +482,7 @@ impl<'tcx> BitwiseOperator for MoveData<'tcx> { impl<'tcx> DataflowOperator for MoveData<'tcx> { #[inline] - fn initial_value(&self) -> bool { + fn initial_value() -> bool { false // no loans in scope by default } } From d03b341e6b9f2c1e665031f510e6129349ecae21 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Thu, 28 Apr 2016 18:51:33 +0200 Subject: [PATCH 09/48] Unit struct defns for 3 dataflow analyses for `borrowck::mir::dataflow`. --- .../borrowck/mir/dataflow.rs | 117 ++++++++++++++++++ 1 file changed, 117 insertions(+) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs index fcdb0c4bf5408..5a508ba9e9610 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow.rs @@ -14,6 +14,7 @@ use rustc::ty::TyCtxt; use rustc::mir::repr::{self, Mir}; use std::io; +use std::marker::PhantomData; use std::mem; use std::usize; @@ -465,6 +466,122 @@ impl DataflowState { } } +// Dataflow analyses are built upon some interpretation of the +// bitvectors attached to each basic block, represented via a +// zero-sized structure. +// +// Note on PhantomData: Each interpretation will need to instantiate +// the `Bit` and `Ctxt` associated types, and in this case, those +// associated types need an associated lifetime `'tcx`. The +// interpretive structures are zero-sized, so they all need to carry a +// `PhantomData` representing how the structures relate to the `'tcx` +// lifetime. +// +// But, since all of the uses of `'tcx` are solely via instances of +// `Ctxt` that are passed into the `BitDenotation` methods, we can +// consistently use a `PhantomData` that is just a function over a +// `&Ctxt` (== `&MoveData<'tcx>). + +/// `MaybeInitializedLvals` tracks all l-values that might be +/// initialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // maybe-init: +/// // {} +/// let a = S; let b = S; let c; let d; // {a, b} +/// +/// if pred { +/// drop(a); // { b} +/// b = S; // { b} +/// +/// } else { +/// drop(b); // {a} +/// d = S; // {a, d} +/// +/// } // {a, b, d} +/// +/// c = S; // {a, b, c, d} +/// } +/// ``` +/// +/// To determine whether an l-value *must* be initialized at a +/// particular control-flow point, one can take the set-difference +/// between this data and the data from `MaybeUninitializedLvals` at the +/// corresponding control-flow point. +/// +/// Similarly, at a given `drop` statement, the set-intersection +/// between this data and `MaybeUninitializedLvals` yields the set of +/// l-values that would require a dynamic drop-flag at that statement. +#[derive(Debug, Default)] +pub struct MaybeInitializedLvals<'tcx> { + // See "Note on PhantomData" above. + phantom: PhantomData Fn(&'a MoveData<'tcx>)>, +} + +/// `MaybeUninitializedLvals` tracks all l-values that might be +/// uninitialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // maybe-uninit: +/// // {a, b, c, d} +/// let a = S; let b = S; let c; let d; // { c, d} +/// +/// if pred { +/// drop(a); // {a, c, d} +/// b = S; // {a, c, d} +/// +/// } else { +/// drop(b); // { b, c, d} +/// d = S; // { b, c } +/// +/// } // {a, b, c, d} +/// +/// c = S; // {a, b, d} +/// } +/// ``` +/// +/// To determine whether an l-value *must* be uninitialized at a +/// particular control-flow point, one can take the set-difference +/// between this data and the data from `MaybeInitializedLvals` at the +/// corresponding control-flow point. +/// +/// Similarly, at a given `drop` statement, the set-intersection +/// between this data and `MaybeInitializedLvals` yields the set of +/// l-values that would require a dynamic drop-flag at that statement. +#[derive(Debug, Default)] +pub struct MaybeUninitializedLvals<'tcx> { + // See "Note on PhantomData" above. + phantom: PhantomData Fn(&'a MoveData<'tcx>)>, +} + +/// `MovingOutStatements` tracks the statements that perform moves out +/// of particular l-values. More precisely, it tracks whether the +/// *effect* of such moves (namely, the uninitialization of the +/// l-value in question) can reach some point in the control-flow of +/// the function, or if that effect is "killed" by some intervening +/// operation reinitializing that l-value. +/// +/// The resulting dataflow is a more enriched version of +/// `MaybeUninitializedLvals`. Both structures on their own only tell +/// you if an l-value *might* be uninitialized at a given point in the +/// control flow. But `MovingOutStatements` also includes the added +/// data of *which* particular statement causing the deinitialization +/// that the borrow checker's error meessage may need to report. +#[derive(Debug, Default)] +pub struct MovingOutStatements<'tcx> { + // See "Note on PhantomData" above. + phantom: PhantomData Fn(&'a MoveData<'tcx>)>, +} impl<'a, 'tcx> DataflowState> { pub fn new_move_analysis(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self { From 6c72c5fa883cc8d33cee90b14fda506eb91d846e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 2 May 2016 13:28:13 +0200 Subject: [PATCH 10/48] `borrowck::mir::gather_moves`: create MovePaths for lvalues even if unreferenced. includes misc bug fixes and removal of useless code. --- .../borrowck/mir/gather_moves.rs | 106 ++++++++++++------ 1 file changed, 74 insertions(+), 32 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index bf3d671bdb5af..64a7dddc8aba2 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -9,7 +9,7 @@ // except according to those terms. -use rustc::ty::TyCtxt; +use rustc::ty::{FnOutput, TyCtxt}; use rustc::mir::repr::*; use rustc::util::nodemap::FnvHashMap; @@ -419,6 +419,11 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> { self.rev_lookup.lookup_proj(proj, base_index) } + fn create_move_path(&mut self, lval: &Lvalue<'tcx>) { + // Create MovePath for `lval`, discarding returned index. + self.move_path_for(lval); + } + fn move_path_for(&mut self, lval: &Lvalue<'tcx>) -> MovePathIndex { let lookup: Lookup = self.lookup(lval); @@ -491,7 +496,7 @@ impl<'a, 'tcx> MoveData<'tcx> { #[derive(Debug)] enum StmtKind { Use, Repeat, Cast, BinaryOp, UnaryOp, Box, - Aggregate, Drop, CallFn, CallArg, Return, + Aggregate, Drop, CallFn, CallArg, Return, If, } fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveData<'tcx> { @@ -511,6 +516,27 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD rev_lookup: MovePathLookup::new(), }; + // Before we analyze the program text, we create the MovePath's + // for all of the vars, args, and temps. (This enforces a basic + // property that even if the MIR body doesn't contain any + // references to a var/arg/temp, it will still be a valid + // operation to lookup the MovePath associated with it.) + assert!(mir.var_decls.len() <= ::std::u32::MAX as usize); + assert!(mir.arg_decls.len() <= ::std::u32::MAX as usize); + assert!(mir.temp_decls.len() <= ::std::u32::MAX as usize); + for var_idx in 0..mir.var_decls.len() { + let path_idx = builder.move_path_for(&Lvalue::Var(var_idx as u32)); + path_map.fill_to(path_idx.idx()); + } + for arg_idx in 0..mir.arg_decls.len() { + let path_idx = builder.move_path_for(&Lvalue::Arg(arg_idx as u32)); + path_map.fill_to(path_idx.idx()); + } + for temp_idx in 0..mir.temp_decls.len() { + let path_idx = builder.move_path_for(&Lvalue::Temp(temp_idx as u32)); + path_map.fill_to(path_idx.idx()); + } + for bb in bbs { let loc_map_bb = &mut loc_map[bb.index()]; let bb_data = mir.basic_block_data(bb); @@ -532,8 +558,12 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD let source = Location { block: bb, index: i }; match stmt.kind { StatementKind::Assign(ref lval, ref rval) => { - // ensure MovePath created for `lval`. - bb_ctxt.builder.move_path_for(lval); + bb_ctxt.builder.create_move_path(lval); + + // Ensure that the path_map contains entries even + // if the lvalue is assigned and never read. + let assigned_path = bb_ctxt.builder.move_path_for(lval); + bb_ctxt.path_map.fill_to(assigned_path.idx()); match *rval { Rvalue::Use(ref operand) => { @@ -569,7 +599,26 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD Rvalue::InlineAsm { .. } => {} Rvalue::Slice {..} => { - bug!("cannot move out of slice"); + // A slice pattern `x..` binds `x` to a + // reference; thus no move occurs. + // + // FIXME: I recall arielb1 questioning + // whether this is even a legal thing to + // have as an R-value. The particular + // example where I am seeing this arise is + // `TargetDataLayout::parse(&Session)` in + // `rustc::ty::layout`. + debug!("encountered Rvalue::Slice as RHS of Assign, source: {:?} \n{}", + source, { + let mut out = Vec::new(); + { + use std::io::Write; + use rustc_mir::pretty::write_mir_named; + let mut w: &mut Write = &mut out; + write_mir_named(tcx, "boo_attempt_move_out_of_slice", mir, &mut w, None).unwrap(); + } + String::from_utf8(out).unwrap() + }); } } } @@ -582,14 +631,19 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD TerminatorKind::Return => { let source = Location { block: bb, index: bb_data.statements.len() }; - let lval = &Lvalue::ReturnPointer.deref(); - bb_ctxt.on_move_out_lval(SK::Return, lval, source); + if let FnOutput::FnConverging(_) = bb_ctxt.builder.mir.return_ty { + debug!("gather_moves Return on_move_out_lval return {:?}", source); + bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source); + } else { + debug!("gather_moves Return on_move_out_lval assuming unreachable return {:?}", source); + } } TerminatorKind::If { ref cond, targets: _ } => { - // The `cond` is always of (copyable) type `bool`, - // so there will never be anything to move. - let _ = cond; + let source = Location { block: bb, + index: bb_data.statements.len() }; + debug!("gather_moves If on_operand {:?} {:?}", cond, source); + bb_ctxt.on_operand(SK::If, cond, source); } TerminatorKind::SwitchInt { switch_ty: _, values: _, targets: _, ref discr } | @@ -604,6 +658,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => { let source = Location { block: bb, index: bb_data.statements.len() }; + debug!("gather_moves Drop on_move_out_lval {:?} {:?}", lval, source); bb_ctxt.on_move_out_lval(SK::Drop, lval, source); } @@ -612,12 +667,18 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD index: bb_data.statements.len() }; bb_ctxt.on_operand(SK::CallFn, func, source); for arg in args { + debug!("gather_moves Call on_operand {:?} {:?}", arg, source); bb_ctxt.on_operand(SK::CallArg, arg, source); } if let Some((ref destination, _bb)) = *destination { - // Create MovePath for `destination`, then - // discard returned index. - bb_ctxt.builder.move_path_for(destination); + debug!("gather_moves Call create_move_path {:?} {:?}", destination, source); + + // Ensure that the path_map contains entries even + // if the lvalue is assigned and never read. + let assigned_path = bb_ctxt.builder.move_path_for(destination); + bb_ctxt.path_map.fill_to(assigned_path.idx()); + + bb_ctxt.builder.create_move_path(destination); } } } @@ -635,7 +696,6 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD // // well you know, lets actually try just asserting that the path map *is* complete. assert_eq!(path_map.len(), builder.pre_move_paths.len()); - path_map.fill_to(builder.pre_move_paths.len() - 1); let pre_move_paths = builder.pre_move_paths; let move_paths: Vec<_> = pre_move_paths.into_iter() @@ -680,24 +740,6 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> { lval: &Lvalue<'tcx>, source: Location) { let tcx = self.tcx; - let lval_ty = self.builder.mir.lvalue_ty(tcx, lval); - - // FIXME: does lvalue_ty ever return TyError, or is it - // guaranteed to always return non-Infer/non-Error values? - - // This code is just trying to avoid creating a MoveOut - // entry for values that do not need move semantics. - // - // type_contents is imprecise (may claim needs drop for - // types that in fact have no destructor). But that is - // still usable for our purposes here. - let consumed = lval_ty.to_ty(tcx).type_contents(tcx).needs_drop(tcx); - - if !consumed { - debug!("ctxt: {:?} no consume of lval: {:?} of type {:?}", - stmt_kind, lval, lval_ty); - return; - } let i = source.index; let index = MoveOutIndex::new(self.moves.len()); From c73f3517a24a87763c7b81228816959ad7fef2e9 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 2 May 2016 15:50:27 +0200 Subject: [PATCH 11/48] Revised mir-dataflow. Incorporates many fixes contributed by arielb1. ---- revise borrowck::mir::dataflow code to allow varying domain for bitvectors. This particular code implements the `BitDenotation` trait for three analyses: * `MovingOutStatements`, which, like `borrowck::move_data`, maps each bit-index to a move instruction, and a 1 means "the effect of this move reaches this point" (and the assigned l-value, if a scoped declaration, is still in scope). * `MaybeInitializedLvals`, which maps each bit-index to an l-value. A 1 means "there exists a control flow path to this point that initializes the associated l-value." * `MaybeUninitializedLvals`, which maps each bit-index to an l-value A 1 means "there exists a control flow path to this point that de-initializes the associated l-value." ---- Revised `graphviz` dataflow-rendering support in `borrowck::mir`. One big difference is that this code is now parameterized over the `BitDenotation`, so that it can be used to render dataflow results independent of how the dataflow bitvectors are interpreted; see where reference to `MoveOut` is replaced by the type parameter `D`. ---- Factor out routine to query subattributes in `#[rustc_mir(..)]`. (Later commits build upon this for some unit testing and instrumentation.) ---- thread through a tcx so that I can query types of lvalues as part of analysis. ---- Revised `BitDenotation::Ctxt`, allowing variation beyond `MoveData`. The main motivation is to ease threading through a `TyCtxt`. (In hindsight it might have been better to instead attach the `TyCtxt` to each of the different dataflow implementations, but that would require e.g. switching away from having a `Default` impl, so I am leaving that experiment for another time.) --- src/librustc/mir/repr.rs | 3 +- src/librustc/mir/transform.rs | 2 +- .../borrowck/mir/dataflow.rs | 629 ---------- .../borrowck/mir/{ => dataflow}/graphviz.rs | 140 ++- .../borrowck/mir/dataflow/mod.rs | 1024 +++++++++++++++++ .../borrowck/mir/gather_moves.rs | 57 +- src/librustc_borrowck/borrowck/mir/mod.rs | 233 +++- 7 files changed, 1368 insertions(+), 720 deletions(-) delete mode 100644 src/librustc_borrowck/borrowck/mir/dataflow.rs rename src/librustc_borrowck/borrowck/mir/{ => dataflow}/graphviz.rs (58%) create mode 100644 src/librustc_borrowck/borrowck/mir/dataflow/mod.rs diff --git a/src/librustc/mir/repr.rs b/src/librustc/mir/repr.rs index 458cb28144adb..f9a671435ffdb 100644 --- a/src/librustc/mir/repr.rs +++ b/src/librustc/mir/repr.rs @@ -226,7 +226,8 @@ pub struct UpvarDecl { /// list of the `Mir`. /// /// (We use a `u32` internally just to save memory.) -#[derive(Copy, Clone, PartialEq, Eq, Hash, RustcEncodable, RustcDecodable)] +#[derive(Copy, Clone, PartialEq, Eq, Hash, PartialOrd, Ord, + RustcEncodable, RustcDecodable)] pub struct BasicBlock(u32); impl BasicBlock { diff --git a/src/librustc/mir/transform.rs b/src/librustc/mir/transform.rs index 79c44b2b851c7..828a48532a2fc 100644 --- a/src/librustc/mir/transform.rs +++ b/src/librustc/mir/transform.rs @@ -18,7 +18,7 @@ use ty::TyCtxt; use syntax::ast::NodeId; /// Where a specific Mir comes from. -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub enum MirSource { /// Functions and methods. Fn(NodeId), diff --git a/src/librustc_borrowck/borrowck/mir/dataflow.rs b/src/librustc_borrowck/borrowck/mir/dataflow.rs deleted file mode 100644 index 5a508ba9e9610..0000000000000 --- a/src/librustc_borrowck/borrowck/mir/dataflow.rs +++ /dev/null @@ -1,629 +0,0 @@ -// Copyright 2012-2016 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. - -use syntax::attr::AttrMetaMethods; - -use rustc::ty::TyCtxt; -use rustc::mir::repr::{self, Mir}; - -use std::io; -use std::marker::PhantomData; -use std::mem; -use std::usize; - -use super::MirBorrowckCtxt; -use super::gather_moves::{Location, MoveData, MovePathData, MovePathIndex, MoveOutIndex, PathMap}; -use super::graphviz; -use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep. - -pub trait Dataflow { - fn dataflow(&mut self); -} - -impl<'b, 'a: 'b, 'tcx: 'a> Dataflow for MirBorrowckCtxt<'b, 'a, 'tcx> { - fn dataflow(&mut self) { - self.build_gen_and_kill_sets(); - self.pre_dataflow_instrumentation().unwrap(); - self.propagate(); - self.post_dataflow_instrumentation().unwrap(); - } -} - -struct PropagationContext<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn> - where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue) -{ - mbcx: &'c mut MirBorrowckCtxt<'b, 'a, 'tcx>, - changed: bool, - on_return: OnReturn -} - -impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> { - fn propagate(&mut self) { - let mut temp = vec![0; self.flow_state.sets.words_per_block]; - let mut propcx = PropagationContext { - mbcx: &mut *self, - changed: true, - on_return: |move_data, in_out, dest_lval| { - let move_path_index = move_data.rev_lookup.find(dest_lval); - on_all_children_bits(in_out, - &move_data.path_map, - &move_data.move_paths, - move_path_index, - &|in_out, mpi| { - in_out.clear_bit(mpi.idx()); - }); - }, - }; - while propcx.changed { - propcx.changed = false; - propcx.reset(&mut temp); - propcx.walk_cfg(&mut temp); - } - } - - fn build_gen_and_kill_sets(&mut self) { - // First we need to build the gen- and kill-sets. The - // gather_moves information provides a high-level mapping from - // mir-locations to the MoveOuts (and those correspond - // directly to gen-sets here). But we still need to figure out - // the kill-sets. - - let move_data = &self.flow_state.operator; - let move_paths = &move_data.move_paths; - let loc_map = &move_data.loc_map; - let path_map = &move_data.path_map; - let rev_lookup = &move_data.rev_lookup; - - for bb in self.mir.all_basic_blocks() { - let &repr::BasicBlockData { ref statements, - ref terminator, - is_cleanup: _ } = - self.mir.basic_block_data(bb); - - let mut sets = self.flow_state.sets.for_block(bb.index()); - for (j, stmt) in statements.iter().enumerate() { - let loc = Location { block: bb, index: j }; - debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}", - stmt, loc, &loc_map[loc]); - for move_index in &loc_map[loc] { - // Every path deinitialized by a *particular move* - // has corresponding bit, "gen'ed" (i.e. set) - // here, in dataflow vector - zero_to_one(&mut sets.gen_set, *move_index); - } - match stmt.kind { - repr::StatementKind::Assign(ref lvalue, _) => { - // assigning into this `lvalue` kills all - // MoveOuts from it, and *also* all MoveOuts - // for children and associated fragment sets. - let move_path_index = rev_lookup.find(lvalue); - - on_all_children_bits(sets.kill_set, - path_map, - move_paths, - move_path_index, - &|kill_set, mpi| { - kill_set.set_bit(mpi.idx()); - }); - } - } - } - - let loc = Location { block: bb, index: statements.len() }; - debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}", - terminator, loc, &loc_map[loc]); - for move_index in &loc_map[loc] { - zero_to_one(&mut sets.gen_set, *move_index); - } - } - - fn zero_to_one(gen_set: &mut [usize], move_index: MoveOutIndex) { - let retval = gen_set.set_bit(move_index.idx()); - assert!(retval); - } - } -} - -fn on_all_children_bits(set: &mut [usize], - path_map: &PathMap, - move_paths: &MovePathData, - move_path_index: MovePathIndex, - each_child: &Each) - where Each: Fn(&mut [usize], MoveOutIndex) -{ - // 1. invoke `each_child` callback for all moves that directly - // influence path for `move_path_index` - for move_index in &path_map[move_path_index] { - each_child(set, *move_index); - } - - // 2. for each child of the path (that is named in this - // function), recur. - // - // (Unnamed children are irrelevant to dataflow; by - // definition they have no associated moves.) - let mut next_child_index = move_paths[move_path_index].first_child; - while let Some(child_index) = next_child_index { - on_all_children_bits(set, path_map, move_paths, child_index, each_child); - next_child_index = move_paths[child_index].next_sibling; - } -} - -impl<'c, 'b: 'c, 'a: 'b, 'tcx: 'a, OnReturn> PropagationContext<'c, 'b, 'a, 'tcx, OnReturn> - where OnReturn: Fn(&MoveData, &mut [usize], &repr::Lvalue) -{ - fn reset(&mut self, bits: &mut [usize]) { - let e = if MoveData::initial_value() {usize::MAX} else {0}; - for b in bits { - *b = e; - } - } - - fn walk_cfg(&mut self, in_out: &mut [usize]) { - let &mut MirBorrowckCtxt { ref mir, ref mut flow_state, .. } = self.mbcx; - for (idx, bb) in mir.basic_blocks.iter().enumerate() { - { - let sets = flow_state.sets.for_block(idx); - debug_assert!(in_out.len() == sets.on_entry.len()); - in_out.clone_from_slice(sets.on_entry); - bitwise(in_out, sets.gen_set, &Union); - bitwise(in_out, sets.kill_set, &Subtract); - } - flow_state.propagate_bits_into_graph_successors_of(in_out, - &mut self.changed, - bb, - &self.on_return); - } - } -} - -impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> { - fn pre_dataflow_instrumentation(&self) -> io::Result<()> { - self.if_attr_meta_name_found( - "borrowck_graphviz_preflow", - |this, path: &str| { - graphviz::print_borrowck_graph_to(this, "preflow", path) - }) - } - - fn post_dataflow_instrumentation(&self) -> io::Result<()> { - self.if_attr_meta_name_found( - "borrowck_graphviz_postflow", - |this, path: &str| { - graphviz::print_borrowck_graph_to(this, "postflow", path) - }) - } - - fn if_attr_meta_name_found(&self, - name: &str, - callback: F) -> io::Result<()> - where F: for <'aa, 'bb> FnOnce(&'aa Self, &'bb str) -> io::Result<()> - { - for attr in self.attributes { - if attr.check_name("rustc_mir") { - let items = attr.meta_item_list(); - for item in items.iter().flat_map(|l| l.iter()) { - if item.check_name(name) { - if let Some(s) = item.value_str() { - return callback(self, &s); - } else { - self.bcx.tcx.sess.span_err( - item.span, - &format!("{} attribute requires a path", item.name())); - } - } - } - } - } - - Ok(()) - } -} - -/// Maps each block to a set of bits -#[derive(Clone, Debug)] -struct Bits { - bits: Vec, -} - -impl Bits { - fn new(init_word: usize, num_words: usize) -> Self { - Bits { bits: vec![init_word; num_words] } - } -} - -pub struct DataflowState -{ - /// All the sets for the analysis. (Factored into its - /// own structure so that we can borrow it mutably - /// on its own separate from other fields.) - pub sets: AllSets, - - /// operator used to initialize, combine, and interpret bits. - operator: O, -} - -pub struct AllSets { - /// Analysis bitwidth for each block. - bits_per_block: usize, - - /// Number of words associated with each block entry - /// equal to bits_per_block / usize::BITS, rounded up. - words_per_block: usize, - - /// For each block, bits generated by executing the statements in - /// the block. (For comparison, the Terminator for each block is - /// handled in a flow-specific manner during propagation.) - gen_sets: Bits, - - /// For each block, bits killed by executing the statements in the - /// block. (For comparison, the Terminator for each block is - /// handled in a flow-specific manner during propagation.) - kill_sets: Bits, - - /// For each block, bits valid on entry to the block. - on_entry_sets: Bits, -} - -pub struct BlockSets<'a> { - on_entry: &'a mut [usize], - gen_set: &'a mut [usize], - kill_set: &'a mut [usize], -} - -impl AllSets { - pub fn bits_per_block(&self) -> usize { self.bits_per_block } - pub fn bytes_per_block(&self) -> usize { (self.bits_per_block + 7) / 8 } - pub fn for_block(&mut self, block_idx: usize) -> BlockSets { - let offset = self.words_per_block * block_idx; - let range = offset..(offset + self.words_per_block); - BlockSets { - on_entry: &mut self.on_entry_sets.bits[range.clone()], - gen_set: &mut self.gen_sets.bits[range.clone()], - kill_set: &mut self.kill_sets.bits[range], - } - } - - fn lookup_set_for<'a>(&self, sets: &'a Bits, block_idx: usize) -> &'a [usize] { - let offset = self.words_per_block * block_idx; - &sets.bits[offset..(offset + self.words_per_block)] - } - pub fn gen_set_for(&self, block_idx: usize) -> &[usize] { - self.lookup_set_for(&self.gen_sets, block_idx) - } - pub fn kill_set_for(&self, block_idx: usize) -> &[usize] { - self.lookup_set_for(&self.kill_sets, block_idx) - } - pub fn on_entry_set_for(&self, block_idx: usize) -> &[usize] { - self.lookup_set_for(&self.on_entry_sets, block_idx) - } - pub fn on_exit_set_for(&self, block_idx: usize) -> Vec { - let mut set: Vec<_> = self.on_entry_set_for(block_idx).iter() - .map(|x|*x) - .collect(); - bitwise(&mut set[..], self.gen_set_for(block_idx), &Union); - bitwise(&mut set[..], self.kill_set_for(block_idx), &Subtract); - return set; - } -} - -impl DataflowState { - fn each_bit(&self, words: &[usize], mut f: F) - where F: FnMut(usize) { - //! Helper for iterating over the bits in a bitvector. - - for (word_index, &word) in words.iter().enumerate() { - if word != 0 { - let usize_bits: usize = mem::size_of::(); - let base_index = word_index * usize_bits; - for offset in 0..usize_bits { - let bit = 1 << offset; - if (word & bit) != 0 { - // NB: we round up the total number of bits - // that we store in any given bit set so that - // it is an even multiple of usize::BITS. This - // means that there may be some stray bits at - // the end that do not correspond to any - // actual value; that's why we first check - // that we are in range of bits_per_block. - let bit_index = base_index + offset as usize; - if bit_index >= self.sets.bits_per_block() { - return; - } else { - f(bit_index); - } - } - } - } - } - } - - pub fn interpret_set(&self, words: &[usize]) -> Vec<&O::Bit> { - let mut v = Vec::new(); - self.each_bit(words, |i| { - v.push(self.operator.interpret(i)); - }); - v - } -} - -pub trait BitwiseOperator { - /// Joins two predecessor bits together, typically either `|` or `&` - fn join(&self, pred1: usize, pred2: usize) -> usize; -} - -/// Parameterization for the precise form of data flow that is used. -pub trait DataflowOperator : BitwiseOperator { - /// Specifies the initial value for each bit in the `on_entry` set - fn initial_value() -> bool; -} - -pub trait BitDenotation: DataflowOperator { - /// Specifies what is represented by each bit in the dataflow bitvector. - type Bit; - /// Size of each bivector allocated for each block in the analysis. - fn bits_per_block(&self) -> usize; - /// Provides the meaning of each entry in the dataflow bitvector. - /// (Mostly intended for use for better debug instrumentation.) - fn interpret(&self, idx: usize) -> &Self::Bit; -} - -impl DataflowState { - pub fn new(mir: &Mir, denotation: D) -> Self { - let bits_per_block = denotation.bits_per_block(); - let usize_bits = mem::size_of::() * 8; - let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits; - let num_blocks = mir.basic_blocks.len(); - let num_words = num_blocks * words_per_block; - - let entry = if D::initial_value() { usize::MAX } else {0}; - - let zeroes = Bits::new(0, num_words); - let on_entry = Bits::new(entry, num_words); - - DataflowState { - sets: AllSets { - bits_per_block: bits_per_block, - words_per_block: words_per_block, - gen_sets: zeroes.clone(), - kill_sets: zeroes, - on_entry_sets: on_entry, - }, - operator: denotation, - } - } -} - -impl DataflowState { - /// Propagates the bits of `in_out` into all the successors of `bb`, - /// using bitwise operator denoted by `self.operator`. - /// - /// For most blocks, this is entirely uniform. However, for blocks - /// that end with a call terminator, the effect of the call on the - /// dataflow state may depend on whether the call returned - /// successfully or unwound. To reflect this, the `on_return` - /// callback mutates `in_out` when propagating `in_out` via a call - /// terminator; such mutation is performed *last*, to ensure its - /// side-effects do not leak elsewhere (e.g. into unwind target). - fn propagate_bits_into_graph_successors_of( - &mut self, - in_out: &mut [usize], - changed: &mut bool, - bb: &repr::BasicBlockData, - on_return: OnReturn) where OnReturn: Fn(&D, &mut [usize], &repr::Lvalue) - { - match bb.terminator().kind { - repr::TerminatorKind::Return | - repr::TerminatorKind::Resume => {} - repr::TerminatorKind::Goto { ref target } | - repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => { - self.propagate_bits_into_entry_set_for(in_out, changed, target); - } - repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => { - self.propagate_bits_into_entry_set_for(in_out, changed, target); - self.propagate_bits_into_entry_set_for(in_out, changed, unwind); - } - repr::TerminatorKind::If { ref targets, .. } => { - self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0); - self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1); - } - repr::TerminatorKind::Switch { ref targets, .. } | - repr::TerminatorKind::SwitchInt { ref targets, .. } => { - for target in targets { - self.propagate_bits_into_entry_set_for(in_out, changed, target); - } - } - repr::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => { - if let Some(ref unwind) = *cleanup { - self.propagate_bits_into_entry_set_for(in_out, changed, unwind); - } - if let Some((ref dest_lval, ref dest_bb)) = *destination { - // N.B.: This must be done *last*, after all other - // propagation, as documented in comment above. - on_return(&self.operator, in_out, dest_lval); - self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb); - } - } - } - } - - fn propagate_bits_into_entry_set_for(&mut self, - in_out: &[usize], - changed: &mut bool, - bb: &repr::BasicBlock) { - let entry_set = self.sets.for_block(bb.index()).on_entry; - let set_changed = bitwise(entry_set, in_out, &self.operator); - if set_changed { - *changed = true; - } - } -} - -// Dataflow analyses are built upon some interpretation of the -// bitvectors attached to each basic block, represented via a -// zero-sized structure. -// -// Note on PhantomData: Each interpretation will need to instantiate -// the `Bit` and `Ctxt` associated types, and in this case, those -// associated types need an associated lifetime `'tcx`. The -// interpretive structures are zero-sized, so they all need to carry a -// `PhantomData` representing how the structures relate to the `'tcx` -// lifetime. -// -// But, since all of the uses of `'tcx` are solely via instances of -// `Ctxt` that are passed into the `BitDenotation` methods, we can -// consistently use a `PhantomData` that is just a function over a -// `&Ctxt` (== `&MoveData<'tcx>). - -/// `MaybeInitializedLvals` tracks all l-values that might be -/// initialized upon reaching a particular point in the control flow -/// for a function. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // maybe-init: -/// // {} -/// let a = S; let b = S; let c; let d; // {a, b} -/// -/// if pred { -/// drop(a); // { b} -/// b = S; // { b} -/// -/// } else { -/// drop(b); // {a} -/// d = S; // {a, d} -/// -/// } // {a, b, d} -/// -/// c = S; // {a, b, c, d} -/// } -/// ``` -/// -/// To determine whether an l-value *must* be initialized at a -/// particular control-flow point, one can take the set-difference -/// between this data and the data from `MaybeUninitializedLvals` at the -/// corresponding control-flow point. -/// -/// Similarly, at a given `drop` statement, the set-intersection -/// between this data and `MaybeUninitializedLvals` yields the set of -/// l-values that would require a dynamic drop-flag at that statement. -#[derive(Debug, Default)] -pub struct MaybeInitializedLvals<'tcx> { - // See "Note on PhantomData" above. - phantom: PhantomData Fn(&'a MoveData<'tcx>)>, -} - -/// `MaybeUninitializedLvals` tracks all l-values that might be -/// uninitialized upon reaching a particular point in the control flow -/// for a function. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // maybe-uninit: -/// // {a, b, c, d} -/// let a = S; let b = S; let c; let d; // { c, d} -/// -/// if pred { -/// drop(a); // {a, c, d} -/// b = S; // {a, c, d} -/// -/// } else { -/// drop(b); // { b, c, d} -/// d = S; // { b, c } -/// -/// } // {a, b, c, d} -/// -/// c = S; // {a, b, d} -/// } -/// ``` -/// -/// To determine whether an l-value *must* be uninitialized at a -/// particular control-flow point, one can take the set-difference -/// between this data and the data from `MaybeInitializedLvals` at the -/// corresponding control-flow point. -/// -/// Similarly, at a given `drop` statement, the set-intersection -/// between this data and `MaybeInitializedLvals` yields the set of -/// l-values that would require a dynamic drop-flag at that statement. -#[derive(Debug, Default)] -pub struct MaybeUninitializedLvals<'tcx> { - // See "Note on PhantomData" above. - phantom: PhantomData Fn(&'a MoveData<'tcx>)>, -} - -/// `MovingOutStatements` tracks the statements that perform moves out -/// of particular l-values. More precisely, it tracks whether the -/// *effect* of such moves (namely, the uninitialization of the -/// l-value in question) can reach some point in the control-flow of -/// the function, or if that effect is "killed" by some intervening -/// operation reinitializing that l-value. -/// -/// The resulting dataflow is a more enriched version of -/// `MaybeUninitializedLvals`. Both structures on their own only tell -/// you if an l-value *might* be uninitialized at a given point in the -/// control flow. But `MovingOutStatements` also includes the added -/// data of *which* particular statement causing the deinitialization -/// that the borrow checker's error meessage may need to report. -#[derive(Debug, Default)] -pub struct MovingOutStatements<'tcx> { - // See "Note on PhantomData" above. - phantom: PhantomData Fn(&'a MoveData<'tcx>)>, -} - -impl<'a, 'tcx> DataflowState> { - pub fn new_move_analysis(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> Self { - let move_data = MoveData::gather_moves(mir, tcx); - DataflowState::new(mir, move_data) - } -} - -impl<'tcx> BitwiseOperator for MoveData<'tcx> { - #[inline] - fn join(&self, pred1: usize, pred2: usize) -> usize { - pred1 | pred2 // moves from both preds are in scope - } -} - -impl<'tcx> DataflowOperator for MoveData<'tcx> { - #[inline] - fn initial_value() -> bool { - false // no loans in scope by default - } -} - -#[inline] -fn bitwise(out_vec: &mut [usize], - in_vec: &[usize], - op: &Op) -> bool { - assert_eq!(out_vec.len(), in_vec.len()); - let mut changed = false; - for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) { - let old_val = *out_elt; - let new_val = op.join(old_val, *in_elt); - *out_elt = new_val; - changed |= old_val != new_val; - } - changed -} - -struct Union; -impl BitwiseOperator for Union { - fn join(&self, a: usize, b: usize) -> usize { a | b } -} -struct Subtract; -impl BitwiseOperator for Subtract { - fn join(&self, a: usize, b: usize) -> usize { a & !b } -} diff --git a/src/librustc_borrowck/borrowck/mir/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs similarity index 58% rename from src/librustc_borrowck/borrowck/mir/graphviz.rs rename to src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs index 460c71dee3d72..bc6ee89fa2577 100644 --- a/src/librustc_borrowck/borrowck/mir/graphviz.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs @@ -10,30 +10,58 @@ //! Hook into libgraphviz for rendering dataflow graphs for MIR. +use syntax::ast::NodeId; use rustc::mir::repr::{BasicBlock, Mir}; use dot; use dot::IntoCow; +use std::fmt::Debug; use std::fs::File; use std::io; use std::io::prelude::*; +use std::marker::PhantomData; +use std::path::Path; -use super::MirBorrowckCtxt; +use super::super::MirBorrowckCtxtPreDataflow; use bitslice::bits_to_string; -use super::gather_moves::MoveOut; +use super::{BitDenotation, DataflowState}; +use super::{HasMoveData}; -struct Graph<'c, 'b:'c, 'a:'b, 'tcx:'a> { mbcx: &'c MirBorrowckCtxt<'b, 'a, 'tcx>, - context: &'b str } +pub trait MirWithFlowState<'tcx> { + type BD: BitDenotation; + fn node_id(&self) -> NodeId; + fn mir(&self) -> &Mir<'tcx>; + fn analysis_ctxt(&self) -> &::Ctxt; + fn flow_state(&self) -> &DataflowState; +} + +impl<'a, 'tcx: 'a, BD> MirWithFlowState<'tcx> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> + where 'a, 'tcx: 'a, BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> +{ + type BD = BD; + fn node_id(&self) -> NodeId { self.node_id } + fn mir(&self) -> &Mir<'tcx> { self.flow_state.mir() } + fn analysis_ctxt(&self) -> &BD::Ctxt { &self.flow_state.ctxt } + fn flow_state(&self) -> &DataflowState { &self.flow_state.flow_state } +} -pub fn print_borrowck_graph_to(mbcx: &MirBorrowckCtxt, - context: &str, - path: &str) -> io::Result<()> { - let g = Graph { mbcx: mbcx, context: context }; +struct Graph<'a, 'tcx, MWF:'a> where MWF: MirWithFlowState<'tcx>, +{ + mbcx: &'a MWF, + phantom: PhantomData<&'tcx ()> +} + +pub fn print_borrowck_graph_to<'a, 'tcx, BD>( + mbcx: &MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>, + path: &Path) -> io::Result<()> where BD: BitDenotation, + BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> +{ + let g = Graph { mbcx: mbcx, phantom: PhantomData }; let mut v = Vec::new(); dot::render(&g, &mut v)?; - println!("print_borrowck_graph_to path: {} context: {} node_id: {}", - path, context, mbcx.node_id); + debug!("print_borrowck_graph_to path: {} node_id: {}", + path.display(), mbcx.node_id); File::create(path).and_then(|mut f| f.write_all(&v)) } @@ -47,13 +75,14 @@ fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec { (0..succ_len).map(|index| Edge { source: bb, index: index}).collect() } -impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> { +impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> + where MWF: MirWithFlowState<'tcx>, ::Bit: Debug +{ type Node = Node; type Edge = Edge; fn graph_id(&self) -> dot::Id { - dot::Id::new(format!("graph_for_node_{}_{}", - self.mbcx.node_id, - self.context)) + dot::Id::new(format!("graph_for_node_{}", + self.mbcx.node_id())) .unwrap() } @@ -106,10 +135,10 @@ impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> { const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#; const ALIGN_RIGHT: &'static str = r#"align="right""#; const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#; - fn chunked_present_left(w: &mut W, - interpreted: &[&MoveOut], - chunk_size: usize) - -> io::Result<()> + fn chunked_present_left(w: &mut W, + interpreted: &[&D], + chunk_size: usize) + -> io::Result<()> { // This function may emit a sequence of 's, but it // always finishes with an (unfinished) @@ -137,40 +166,55 @@ impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> { Ok(()) } ::rustc_mir::graphviz::write_node_label( - *n, self.mbcx.mir, &mut v, 4, + *n, self.mbcx.mir(), &mut v, 4, |w| { - let flow = &self.mbcx.flow_state; - let entry = flow.interpret_set(flow.sets.on_entry_set_for(i)); - chunked_present_left(w, &entry[..], chunk_size)?; + let ctxt = self.mbcx.analysis_ctxt(); + let flow = self.mbcx.flow_state(); + let entry_interp = flow.interpret_set(ctxt, flow.sets.on_entry_set_for(i)); + chunked_present_left(w, &entry_interp[..], chunk_size)?; + let bits_per_block = flow.sets.bits_per_block(); + let entry = flow.sets.on_entry_set_for(i); + debug!("entry set for i={i} bits_per_block: {bpb} entry: {e:?} interp: {ei:?}", + i=i, e=entry, bpb=bits_per_block, ei=entry_interp); write!(w, "= ENTRY:{entrybits:?}\ ", bg = BG_FLOWCONTENT, face = FACE_MONOSPACE, - entrybits=bits_to_string(flow.sets.on_entry_set_for(i), - flow.sets.bytes_per_block())) + entrybits=bits_to_string(entry, bits_per_block)) }, |w| { - let flow = &self.mbcx.flow_state; - let gen = flow.interpret_set( flow.sets.gen_set_for(i)); - let kill = flow.interpret_set(flow.sets.kill_set_for(i)); - chunked_present_left(w, &gen[..], chunk_size)?; - write!(w, " = GEN:{genbits:?}\ - ", - bg = BG_FLOWCONTENT, - face = FACE_MONOSPACE, - genbits=bits_to_string( flow.sets.gen_set_for(i), - flow.sets.bytes_per_block()))?; - write!(w, "KILL:\ - {killbits:?}", - bg = BG_FLOWCONTENT, - align = ALIGN_RIGHT, - face = FACE_MONOSPACE, - killbits=bits_to_string(flow.sets.kill_set_for(i), - flow.sets.bytes_per_block()))?; + let ctxt = self.mbcx.analysis_ctxt(); + let flow = self.mbcx.flow_state(); + let gen_interp = flow.interpret_set(ctxt, flow.sets.gen_set_for(i)); + let kill_interp = flow.interpret_set(ctxt, flow.sets.kill_set_for(i)); + chunked_present_left(w, &gen_interp[..], chunk_size)?; + let bits_per_block = flow.sets.bits_per_block(); + { + let gen = flow.sets.gen_set_for(i); + debug!("gen set for i={i} bits_per_block: {bpb} gen: {g:?} interp: {gi:?}", + i=i, g=gen, bpb=bits_per_block, gi=gen_interp); + write!(w, " = GEN:{genbits:?}\ + ", + bg = BG_FLOWCONTENT, + face = FACE_MONOSPACE, + genbits=bits_to_string(gen, bits_per_block))?; + } + + { + let kill = flow.sets.kill_set_for(i); + debug!("kill set for i={i} bits_per_block: {bpb} kill: {k:?} interp: {ki:?}", + i=i, k=kill, bpb=bits_per_block, ki=kill_interp); + write!(w, "KILL:\ + {killbits:?}", + bg = BG_FLOWCONTENT, + align = ALIGN_RIGHT, + face = FACE_MONOSPACE, + killbits=bits_to_string(kill, bits_per_block))?; + } // (chunked_present_right) let mut seen_one = false; - for k in kill.chunks(chunk_size) { + for k in kill_interp.chunks(chunk_size) { if !seen_one { // continuation of row; this is fourth write!(w, "= {kill:?}", @@ -200,16 +244,18 @@ impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::Labeller<'c> for Graph<'c,'b,'a,'tcx> { } } -impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::GraphWalk<'c> for Graph<'c,'b,'a,'tcx> { +impl<'a, 'tcx, MWF> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF> + where MWF: MirWithFlowState<'tcx> +{ type Node = Node; type Edge = Edge; fn nodes(&self) -> dot::Nodes { - self.mbcx.mir.all_basic_blocks().into_cow() + self.mbcx.mir().all_basic_blocks().into_cow() } fn edges(&self) -> dot::Edges { - let mir = self.mbcx.mir; - let blocks = self.mbcx.mir.all_basic_blocks(); + let mir = self.mbcx.mir(); + let blocks = mir.all_basic_blocks(); // base initial capacity on assumption every block has at // least one outgoing edge (Which should be true for all // blocks but one, the exit-block). @@ -226,7 +272,7 @@ impl<'c, 'b:'c, 'a:'b, 'tcx:'a> dot::GraphWalk<'c> for Graph<'c,'b,'a,'tcx> { } fn target(&self, edge: &Edge) -> Node { - let mir = self.mbcx.mir; + let mir = self.mbcx.mir(); mir.basic_block_data(edge.source).terminator().successors()[edge.index] } } diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs new file mode 100644 index 0000000000000..41fe6079526cf --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -0,0 +1,1024 @@ +// Copyright 2012-2016 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. + +use rustc::ty::TyCtxt; +use rustc::mir::repr::{self, Mir}; + +use std::fmt::Debug; +use std::io; +use std::marker::PhantomData; +use std::mem; +use std::path::PathBuf; +use std::usize; + +use super::MirBorrowckCtxtPreDataflow; +use super::gather_moves::{Location, MoveData, MovePathData, MovePathIndex, MoveOutIndex, PathMap}; +use super::gather_moves::{MoveOut, MovePath}; +use super::DropFlagState; + +use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep. + +mod graphviz; + +pub trait Dataflow { + fn dataflow(&mut self); +} + +impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> + where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> +{ + fn dataflow(&mut self) { + self.flow_state.build_sets(); + self.pre_dataflow_instrumentation().unwrap(); + self.flow_state.propagate(); + self.post_dataflow_instrumentation().unwrap(); + } +} + +struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> + where O: 'b + BitDenotation, O::Ctxt: HasMoveData<'tcx>, +{ + builder: &'b mut DataflowAnalysis<'a, 'tcx, O>, + changed: bool, +} + +impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> + where BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> +{ + fn propagate(&mut self) { + let mut temp = vec![0; self.flow_state.sets.words_per_block]; + let mut propcx = PropagationContext { + builder: self, + changed: true, + }; + while propcx.changed { + propcx.changed = false; + propcx.reset(&mut temp); + propcx.walk_cfg(&mut temp); + } + } + + fn build_sets(&mut self) { + // First we need to build the entry-, gen- and kill-sets. The + // gather_moves information provides a high-level mapping from + // mir-locations to the MoveOuts (and those correspond + // directly to gen-sets here). But we still need to figure out + // the kill-sets. + + { + let sets = &mut self.flow_state.sets.for_block(repr::START_BLOCK.index()); + self.flow_state.operator.start_block_effect(&self.ctxt, sets); + } + + for bb in self.mir.all_basic_blocks() { + let &repr::BasicBlockData { ref statements, + ref terminator, + is_cleanup: _ } = + self.mir.basic_block_data(bb); + + let sets = &mut self.flow_state.sets.for_block(bb.index()); + for j_stmt in 0..statements.len() { + self.flow_state.operator.statement_effect(&self.ctxt, sets, bb, j_stmt); + } + + if terminator.is_some() { + let stmts_len = statements.len(); + self.flow_state.operator.terminator_effect(&self.ctxt, sets, bb, stmts_len); + } + } + } +} + +fn on_all_children_bits(path_map: &PathMap, + move_paths: &MovePathData, + move_path_index: MovePathIndex, + mut each_child: Each) + where Each: FnMut(MoveOutIndex) +{ + return on_all_children_bits_recur( + path_map, move_paths, move_path_index, &mut each_child); + + fn on_all_children_bits_recur(path_map: &PathMap, + move_paths: &MovePathData, + move_path_index: MovePathIndex, + each_child: &mut Each) + where Each: FnMut(MoveOutIndex) + { + // 1. invoke `each_child` callback for all moves that directly + // influence path for `move_path_index` + for move_index in &path_map[move_path_index] { + each_child(*move_index); + } + + // 2. for each child of the path (that is named in this + // function), recur. + // + // (Unnamed children are irrelevant to dataflow; by + // definition they have no associated moves.) + let mut next_child_index = move_paths[move_path_index].first_child; + while let Some(child_index) = next_child_index { + on_all_children_bits_recur(path_map, move_paths, child_index, each_child); + next_child_index = move_paths[child_index].next_sibling; + } + } +} + +impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> + where BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> +{ + fn reset(&mut self, bits: &mut [usize]) { + let e = if BD::initial_value() {usize::MAX} else {0}; + for b in bits { + *b = e; + } + } + + fn walk_cfg(&mut self, in_out: &mut [usize]) { + let mir = self.builder.mir; + for (bb_idx, bb_data) in mir.basic_blocks.iter().enumerate() { + let builder = &mut self.builder; + { + let sets = builder.flow_state.sets.for_block(bb_idx); + debug_assert!(in_out.len() == sets.on_entry.len()); + in_out.clone_from_slice(sets.on_entry); + bitwise(in_out, sets.gen_set, &Union); + bitwise(in_out, sets.kill_set, &Subtract); + } + builder.propagate_bits_into_graph_successors_of(in_out, + &mut self.changed, + (repr::BasicBlock::new(bb_idx), bb_data)); + } + } +} + +impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> + where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> +{ + fn path(context: &str, prepost: &str, path: &str) -> PathBuf { + format!("{}_{}", context, prepost); + let mut path = PathBuf::from(path); + let new_file_name = { + let orig_file_name = path.file_name().unwrap().to_str().unwrap(); + format!("{}_{}", context, orig_file_name) + }; + path.set_file_name(new_file_name); + path + } + + fn pre_dataflow_instrumentation(&self) -> io::Result<()> { + if let Some(ref path_str) = self.print_preflow_to { + let path = Self::path(BD::name(), "preflow", path_str); + graphviz::print_borrowck_graph_to(self, &path) + } else { + Ok(()) + } + } + + fn post_dataflow_instrumentation(&self) -> io::Result<()> { + if let Some(ref path_str) = self.print_postflow_to { + let path = Self::path(BD::name(), "postflow", path_str); + graphviz::print_borrowck_graph_to(self, &path) + } else{ + Ok(()) + } + } +} + +/// Maps each block to a set of bits +#[derive(Clone, Debug)] +struct Bits { + bits: Vec, +} + +impl Bits { + fn new(init_word: usize, num_words: usize) -> Self { + Bits { bits: vec![init_word; num_words] } + } +} + +pub trait HasMoveData<'tcx> { + fn move_data(&self) -> &MoveData<'tcx>; +} + +impl<'tcx> HasMoveData<'tcx> for MoveData<'tcx> { + fn move_data(&self) -> &MoveData<'tcx> { self } +} +impl<'tcx, A, B> HasMoveData<'tcx> for (A, B, MoveData<'tcx>) { + fn move_data(&self) -> &MoveData<'tcx> { &self.2 } +} + +pub struct DataflowAnalysis<'a, 'tcx: 'a, O> + where O: BitDenotation, O::Ctxt: HasMoveData<'tcx> +{ + flow_state: DataflowState, + ctxt: O::Ctxt, + mir: &'a Mir<'tcx>, +} + +impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> + where O: BitDenotation, O::Ctxt: HasMoveData<'tcx> +{ + pub fn results(self) -> (O::Ctxt, DataflowResults) { + (self.ctxt, DataflowResults(self.flow_state)) + } + + pub fn mir(&self) -> &'a Mir<'tcx> { self.mir } +} + +#[derive(Debug)] +pub struct DataflowResults(DataflowState); + +// 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. +#[derive(Debug)] +pub struct DataflowState +{ + /// All the sets for the analysis. (Factored into its + /// own structure so that we can borrow it mutably + /// on its own separate from other fields.) + pub sets: AllSets, + + /// operator used to initialize, combine, and interpret bits. + operator: O, +} + +#[derive(Debug)] +pub struct AllSets { + /// Analysis bitwidth for each block. + bits_per_block: usize, + + /// Number of words associated with each block entry + /// equal to bits_per_block / usize::BITS, rounded up. + words_per_block: usize, + + /// For each block, bits generated by executing the statements in + /// the block. (For comparison, the Terminator for each block is + /// handled in a flow-specific manner during propagation.) + gen_sets: Bits, + + /// For each block, bits killed by executing the statements in the + /// block. (For comparison, the Terminator for each block is + /// handled in a flow-specific manner during propagation.) + kill_sets: Bits, + + /// For each block, bits valid on entry to the block. + on_entry_sets: Bits, +} + +pub struct BlockSets<'a> { + on_entry: &'a mut [usize], + gen_set: &'a mut [usize], + kill_set: &'a mut [usize], +} + +impl AllSets { + pub fn bits_per_block(&self) -> usize { self.bits_per_block } + pub fn for_block(&mut self, block_idx: usize) -> BlockSets { + let offset = self.words_per_block * block_idx; + let range = offset..(offset + self.words_per_block); + BlockSets { + on_entry: &mut self.on_entry_sets.bits[range.clone()], + gen_set: &mut self.gen_sets.bits[range.clone()], + kill_set: &mut self.kill_sets.bits[range], + } + } + + fn lookup_set_for<'a>(&self, sets: &'a Bits, block_idx: usize) -> &'a [usize] { + let offset = self.words_per_block * block_idx; + &sets.bits[offset..(offset + self.words_per_block)] + } + pub fn gen_set_for(&self, block_idx: usize) -> &[usize] { + self.lookup_set_for(&self.gen_sets, block_idx) + } + pub fn kill_set_for(&self, block_idx: usize) -> &[usize] { + self.lookup_set_for(&self.kill_sets, block_idx) + } + pub fn on_entry_set_for(&self, block_idx: usize) -> &[usize] { + self.lookup_set_for(&self.on_entry_sets, block_idx) + } +} + +impl DataflowState { + fn each_bit(&self, ctxt: &O::Ctxt, words: &[usize], mut f: F) + where F: FnMut(usize) { + //! Helper for iterating over the bits in a bitvector. + + let bits_per_block = self.operator.bits_per_block(ctxt); + let usize_bits: usize = mem::size_of::() * 8; + + for (word_index, &word) in words.iter().enumerate() { + if word != 0 { + let base_index = word_index * usize_bits; + for offset in 0..usize_bits { + let bit = 1 << offset; + if (word & bit) != 0 { + // NB: we round up the total number of bits + // that we store in any given bit set so that + // it is an even multiple of usize::BITS. This + // means that there may be some stray bits at + // the end that do not correspond to any + // actual value; that's why we first check + // that we are in range of bits_per_block. + let bit_index = base_index + offset as usize; + if bit_index >= bits_per_block { + return; + } else { + f(bit_index); + } + } + } + } + } + } + + pub fn interpret_set<'c>(&self, ctxt: &'c O::Ctxt, words: &[usize]) -> Vec<&'c O::Bit> { + let mut v = Vec::new(); + self.each_bit(ctxt, words, |i| { + v.push(self.operator.interpret(ctxt, i)); + }); + v + } +} + +pub trait BitwiseOperator { + /// Joins two predecessor bits together, typically either `|` or `&` + fn join(&self, pred1: usize, pred2: usize) -> usize; +} + +/// Parameterization for the precise form of data flow that is used. +pub trait DataflowOperator : BitwiseOperator { + /// Specifies the initial value for each bit in the `on_entry` set + fn initial_value() -> bool; +} + +pub trait BitDenotation: DataflowOperator { + /// Specifies what is represented by each bit in the dataflow bitvector. + type Bit; + + /// Specifies what, if any, separate context needs to be supplied for methods below. + type Ctxt; + + /// A name describing the dataflow analysis that this + /// BitDenotation is supporting. The name should be something + /// suitable for plugging in as part of a filename e.g. avoid + /// space-characters or other things that tend to look bad on a + /// file system, like slashes or periods. It is also better for + /// the name to be reasonably short, again because it will be + /// plugged into a filename. + fn name() -> &'static str; + + /// Size of each bitvector allocated for each block in the analysis. + fn bits_per_block(&self, &Self::Ctxt) -> usize; + + /// Provides the meaning of each entry in the dataflow bitvector. + /// (Mostly intended for use for better debug instrumentation.) + fn interpret<'a>(&self, &'a Self::Ctxt, idx: usize) -> &'a Self::Bit; + + /// Mutates the block-sets (the flow sets for the given + /// basic block) according to the effects that have been + /// established *prior* to entering the start block. + /// + /// (For example, establishing the call arguments.) + /// + /// (Typically this should only modify `sets.on_entry`, since the + /// gen and kill sets should reflect the effects of *executing* + /// the start block itself.) + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets); + + /// Mutates the block-sets (the flow sets for the given + /// basic block) according to the effects of evaluating statement. + /// + /// This is used, in particular, for building up the + /// "transfer-function" represnting the overall-effect of the + /// block, represented via GEN and KILL sets. + /// + /// The statement here is `idx_stmt.1`; `idx_stmt.0` is just + /// an identifying index: namely, the index of the statement + /// in the basic block. + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx_stmt: usize); + + /// Mutates the block-sets (the flow sets for the given + /// basic block) according to the effects of evaluating + /// the terminator. + /// + /// This is used, in particular, for building up the + /// "transfer-function" represnting the overall-effect of the + /// block, represented via GEN and KILL sets. + /// + /// The terminator here is `idx_term.1`; `idx_term.0` is just an + /// identifying index: namely, the number of statements in `bb` + /// itself. + /// + /// The effects applied here cannot depend on which branch the + /// terminator took. + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx_term: usize); + + /// Mutates the block-sets according to the (flow-dependent) + /// effect of a successful return from a Call terminator. + /// + /// If basic-block BB_x ends with a call-instruction that, upon + /// successful return, flows to BB_y, then this method will be + /// called on the exit flow-state of BB_x in order to set up the + /// entry flow-state of BB_y. + /// + /// This is used, in particular, as a special case during the + /// "propagate" loop where all of the basic blocks are repeatedly + /// visited. Since the effects of a Call terminator are + /// flow-dependent, the current MIR cannot encode them via just + /// GEN and KILL sets attached to the block, and so instead we add + /// this extra machinery to represent the flow-dependent effect. + /// + /// Note: as a historical artifact, this currently takes as input + /// the *entire* packed collection of bitvectors in `in_out`. We + /// might want to look into narrowing that to something more + /// specific, just to make the interface more self-documenting. + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut [usize], + call_bb: repr::BasicBlock, + dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue); +} + +impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> + where D: BitDenotation, D::Ctxt: HasMoveData<'tcx> +{ + pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, + ctxt: D::Ctxt, + denotation: D) -> Self { + let bits_per_block = denotation.bits_per_block(&ctxt); + let usize_bits = mem::size_of::() * 8; + let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits; + let num_blocks = mir.basic_blocks.len(); + let num_words = num_blocks * words_per_block; + + let entry = if D::initial_value() { usize::MAX } else {0}; + + let zeroes = Bits::new(0, num_words); + let on_entry = Bits::new(entry, num_words); + + DataflowAnalysis { flow_state: DataflowState { + sets: AllSets { + bits_per_block: bits_per_block, + words_per_block: words_per_block, + gen_sets: zeroes.clone(), + kill_sets: zeroes, + on_entry_sets: on_entry, + }, + operator: denotation, + }, + ctxt: ctxt, + mir: mir, + } + + } +} + +impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> + where D: BitDenotation, D::Ctxt: HasMoveData<'tcx> +{ + /// Propagates the bits of `in_out` into all the successors of `bb`, + /// using bitwise operator denoted by `self.operator`. + /// + /// For most blocks, this is entirely uniform. However, for blocks + /// that end with a call terminator, the effect of the call on the + /// dataflow state may depend on whether the call returned + /// successfully or unwound. + /// + /// To reflect this, the `propagate_call_return` method of the + /// `BitDenotation` mutates `in_out` when propagating `in_out` via + /// a call terminator; such mutation is performed *last*, to + /// ensure its side-effects do not leak elsewhere (e.g. into + /// unwind target). + fn propagate_bits_into_graph_successors_of( + &mut self, + in_out: &mut [usize], + changed: &mut bool, + (bb, bb_data): (repr::BasicBlock, &repr::BasicBlockData)) + { + match bb_data.terminator().kind { + repr::TerminatorKind::Return | + repr::TerminatorKind::Resume => {} + repr::TerminatorKind::Goto { ref target } | + repr::TerminatorKind::Drop { ref target, value: _, unwind: None } => { + self.propagate_bits_into_entry_set_for(in_out, changed, target); + } + repr::TerminatorKind::Drop { ref target, value: _, unwind: Some(ref unwind) } => { + self.propagate_bits_into_entry_set_for(in_out, changed, target); + self.propagate_bits_into_entry_set_for(in_out, changed, unwind); + } + repr::TerminatorKind::If { ref targets, .. } => { + self.propagate_bits_into_entry_set_for(in_out, changed, &targets.0); + self.propagate_bits_into_entry_set_for(in_out, changed, &targets.1); + } + repr::TerminatorKind::Switch { ref targets, .. } | + repr::TerminatorKind::SwitchInt { ref targets, .. } => { + for target in targets { + self.propagate_bits_into_entry_set_for(in_out, changed, target); + } + } + repr::TerminatorKind::Call { ref cleanup, ref destination, func: _, args: _ } => { + if let Some(ref unwind) = *cleanup { + self.propagate_bits_into_entry_set_for(in_out, changed, unwind); + } + if let Some((ref dest_lval, ref dest_bb)) = *destination { + // N.B.: This must be done *last*, after all other + // propagation, as documented in comment above. + self.flow_state.operator.propagate_call_return( + &self.ctxt, in_out, bb, *dest_bb, dest_lval); + self.propagate_bits_into_entry_set_for(in_out, changed, dest_bb); + } + } + } + } + + fn propagate_bits_into_entry_set_for(&mut self, + in_out: &[usize], + changed: &mut bool, + bb: &repr::BasicBlock) { + let entry_set = self.flow_state.sets.for_block(bb.index()).on_entry; + let set_changed = bitwise(entry_set, in_out, &self.flow_state.operator); + if set_changed { + *changed = true; + } + } +} + +// Dataflow analyses are built upon some interpretation of the +// bitvectors attached to each basic block, represented via a +// zero-sized structure. +// +// Note on PhantomData: Each interpretation will need to instantiate +// the `Bit` and `Ctxt` associated types, and in this case, those +// associated types need an associated lifetime `'tcx`. The +// interpretive structures are zero-sized, so they all need to carry a +// `PhantomData` representing how the structures relate to the `'tcx` +// lifetime. +// +// But, since all of the uses of `'tcx` are solely via instances of +// `Ctxt` that are passed into the `BitDenotation` methods, we can +// consistently use a `PhantomData` that is just a function over a +// `&Ctxt` (== `&MoveData<'tcx>). + +/// `MaybeInitializedLvals` tracks all l-values that might be +/// initialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // maybe-init: +/// // {} +/// let a = S; let b = S; let c; let d; // {a, b} +/// +/// if pred { +/// drop(a); // { b} +/// b = S; // { b} +/// +/// } else { +/// drop(b); // {a} +/// d = S; // {a, d} +/// +/// } // {a, b, d} +/// +/// c = S; // {a, b, c, d} +/// } +/// ``` +/// +/// To determine whether an l-value *must* be initialized at a +/// particular control-flow point, one can take the set-difference +/// between this data and the data from `MaybeUninitializedLvals` at the +/// corresponding control-flow point. +/// +/// Similarly, at a given `drop` statement, the set-intersection +/// between this data and `MaybeUninitializedLvals` yields the set of +/// l-values that would require a dynamic drop-flag at that statement. +#[derive(Debug, Default)] +pub struct MaybeInitializedLvals<'a, 'tcx: 'a> { + // See "Note on PhantomData" above. + phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> +} + +/// `MaybeUninitializedLvals` tracks all l-values that might be +/// uninitialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // maybe-uninit: +/// // {a, b, c, d} +/// let a = S; let b = S; let c; let d; // { c, d} +/// +/// if pred { +/// drop(a); // {a, c, d} +/// b = S; // {a, c, d} +/// +/// } else { +/// drop(b); // { b, c, d} +/// d = S; // { b, c } +/// +/// } // {a, b, c, d} +/// +/// c = S; // {a, b, d} +/// } +/// ``` +/// +/// To determine whether an l-value *must* be uninitialized at a +/// particular control-flow point, one can take the set-difference +/// between this data and the data from `MaybeInitializedLvals` at the +/// corresponding control-flow point. +/// +/// Similarly, at a given `drop` statement, the set-intersection +/// between this data and `MaybeInitializedLvals` yields the set of +/// l-values that would require a dynamic drop-flag at that statement. +#[derive(Debug, Default)] +pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> { + // See "Note on PhantomData" above. + phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> +} + +/// `MovingOutStatements` tracks the statements that perform moves out +/// of particular l-values. More precisely, it tracks whether the +/// *effect* of such moves (namely, the uninitialization of the +/// l-value in question) can reach some point in the control-flow of +/// the function, or if that effect is "killed" by some intervening +/// operation reinitializing that l-value. +/// +/// The resulting dataflow is a more enriched version of +/// `MaybeUninitializedLvals`. Both structures on their own only tell +/// you if an l-value *might* be uninitialized at a given point in the +/// control flow. But `MovingOutStatements` also includes the added +/// data of *which* particular statement causing the deinitialization +/// that the borrow checker's error meessage may need to report. +#[derive(Debug, Default)] +pub struct MovingOutStatements<'a, 'tcx: 'a> { + // See "Note on PhantomData" above. + phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> +} + +impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { + type Bit = MoveOut; + type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + fn name() -> &'static str { "moving_out" } + fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { + ctxt.2.moves.len() + } + fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { + &ctxt.2.moves[idx] + } + fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) { + // no move-statements have been executed prior to function + // execution, so this method has no effect on `_sets`. + } + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx: usize) { + let &(_tcx, mir, ref move_data) = ctxt; + let stmt = &mir.basic_block_data(bb).statements[idx]; + let move_paths = &move_data.move_paths; + let loc_map = &move_data.loc_map; + let path_map = &move_data.path_map; + let rev_lookup = &move_data.rev_lookup; + + let loc = Location { block: bb, index: idx }; + debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}", + stmt, loc, &loc_map[loc]); + for move_index in &loc_map[loc] { + // Every path deinitialized by a *particular move* + // has corresponding bit, "gen'ed" (i.e. set) + // here, in dataflow vector + zero_to_one(&mut sets.gen_set, *move_index); + } + let bits_per_block = self.bits_per_block(ctxt); + match stmt.kind { + repr::StatementKind::Assign(ref lvalue, _) => { + // assigning into this `lvalue` kills all + // MoveOuts from it, and *also* all MoveOuts + // for children and associated fragment sets. + let move_path_index = rev_lookup.find(lvalue); + + sets.kill_set.set_bit(move_path_index.idx()); + on_all_children_bits(path_map, + move_paths, + move_path_index, + |moi| { + assert!(moi.idx() < bits_per_block); + sets.kill_set.set_bit(moi.idx()); + }); + } + } + } + + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + statements_len: usize) + { + let &(_tcx, mir, ref move_data) = ctxt; + let term = mir.basic_block_data(bb).terminator.as_ref().unwrap(); + let loc_map = &move_data.loc_map; + let loc = Location { block: bb, index: statements_len }; + debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}", + term, loc, &loc_map[loc]); + let bits_per_block = self.bits_per_block(ctxt); + for move_index in &loc_map[loc] { + assert!(move_index.idx() < bits_per_block); + zero_to_one(&mut sets.gen_set, *move_index); + } + } + + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut [usize], + _call_bb: repr::BasicBlock, + _dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue) { + let move_data = &ctxt.2; + let move_path_index = move_data.rev_lookup.find(dest_lval); + let bits_per_block = self.bits_per_block(ctxt); + + in_out.clear_bit(move_path_index.idx()); + on_all_children_bits(&move_data.path_map, + &move_data.move_paths, + move_path_index, + |moi| { + assert!(moi.idx() < bits_per_block); + in_out.clear_bit(moi.idx()); + }); + } +} + +impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + state: super::DropFlagState) + { + match state { + DropFlagState::Dead => { + sets.gen_set.clear_bit(path.idx()); + sets.kill_set.set_bit(path.idx()); + } + DropFlagState::Live => { + sets.gen_set.set_bit(path.idx()); + sets.kill_set.clear_bit(path.idx()); + } + } + } +} + +impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + state: super::DropFlagState) + { + match state { + DropFlagState::Dead => { + sets.gen_set.set_bit(path.idx()); + sets.kill_set.clear_bit(path.idx()); + } + DropFlagState::Live => { + sets.gen_set.clear_bit(path.idx()); + sets.kill_set.set_bit(path.idx()); + } + } + } +} + +impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { + type Bit = MovePath<'tcx>; + type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + fn name() -> &'static str { "maybe_init" } + fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { + ctxt.2.move_paths.len() + } + fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { + &ctxt.2.move_paths[MovePathIndex::new(idx)] + } + + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) + { + super::drop_flag_effects_for_function_entry( + ctxt.0, ctxt.1, &ctxt.2, + |path, s| { + assert!(s == DropFlagState::Live); + sets.on_entry.set_bit(path.idx()); + }); + } + + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx: usize) + { + super::drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: idx }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + statements_len: usize) + { + super::drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: statements_len }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut [usize], + _call_bb: repr::BasicBlock, + _dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue) { + // when a call returns successfully, that means we need to set + // the bits for that dest_lval to 1 (initialized). + let move_data = &ctxt.2; + let move_path_index = move_data.rev_lookup.find(dest_lval); + super::on_all_children_bits( + ctxt.0, ctxt.1, &ctxt.2, + move_path_index, + |mpi| { in_out.set_bit(mpi.idx()); } + ); + } +} + +impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { + type Bit = MovePath<'tcx>; + type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + fn name() -> &'static str { "maybe_uninit" } + fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { + ctxt.2.move_paths.len() + } + fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { + &ctxt.2.move_paths[MovePathIndex::new(idx)] + } + + // sets on_entry bits for Arg lvalues + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { + for e in &mut sets.on_entry[..] { *e = !0; } + + super::drop_flag_effects_for_function_entry( + ctxt.0, ctxt.1, &ctxt.2, + |path, s| { + assert!(s == DropFlagState::Live); + sets.on_entry.clear_bit(path.idx()); + }); + } + + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx: usize) + { + super::drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: idx }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + statements_len: usize) + { + super::drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: statements_len }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut [usize], + _call_bb: repr::BasicBlock, + _dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue) { + // when a call returns successfully, that means we need to set + // the bits for that dest_lval to 1 (initialized). + let move_path_index = ctxt.2.rev_lookup.find(dest_lval); + super::on_all_children_bits( + ctxt.0, ctxt.1, &ctxt.2, + move_path_index, + |mpi| { in_out.clear_bit(mpi.idx()); } + ); + } +} + +fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) { + let retval = bitvec.set_bit(move_index.idx()); + assert!(retval); +} + +impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // moves from both preds are in scope + } +} + +impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // "maybe" means we union effects of both preds + } +} + +impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // "maybe" means we union effects of both preds + } +} + +// FIXME: I'm not sure it ever makes sense to use `true` for a +// DataflowOperator::initial_value implementation, because: the way +// that dataflow fixed point iteration works, you want to start at +// bottom and work your way to a fixed point. +// +// This means, for propagation across the graph, that you either want +// to start at all-zeroes and then use Union as your merge when +// propagating, or you start at all-ones and then use Intersect as +// your merge when propagating. +// +// (An alternative could be, when propagating from Block A into block +// B, to clear B's on_entry bits, and then iterate over all of B's +// immediate predecessors. This would require storing on_exit state +// for each block, however.) + +impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> { + #[inline] + fn initial_value() -> bool { + false // bottom = no loans in scope by default + } +} + +impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> { + #[inline] + fn initial_value() -> bool { + false // bottom = uninitialized + } +} + +impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> { + #[inline] + fn initial_value() -> bool { + false // bottom = uninitialized + } +} + +#[inline] +fn bitwise(out_vec: &mut [usize], + in_vec: &[usize], + op: &Op) -> bool { + assert_eq!(out_vec.len(), in_vec.len()); + let mut changed = false; + for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) { + let old_val = *out_elt; + let new_val = op.join(old_val, *in_elt); + *out_elt = new_val; + changed |= old_val != new_val; + } + changed +} + +struct Union; +impl BitwiseOperator for Union { + fn join(&self, a: usize, b: usize) -> usize { a | b } +} +struct Subtract; +impl BitwiseOperator for Subtract { + fn join(&self, a: usize, b: usize) -> usize { a & !b } +} diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 64a7dddc8aba2..519e9398436b8 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -19,7 +19,6 @@ use std::fmt; use std::iter; use std::ops::Index; -use super::dataflow::BitDenotation; use super::abs_domain::{AbstractElem, Lift}; // This submodule holds some newtype'd Index wrappers that are using @@ -32,7 +31,7 @@ mod indexes { macro_rules! new_index { ($Index:ident) => { - #[derive(Copy, Clone, PartialEq, Eq, Debug)] + #[derive(Copy, Clone, PartialEq, Eq, Debug, Hash)] pub struct $Index(NonZero); impl $Index { @@ -56,6 +55,12 @@ mod indexes { pub use self::indexes::MovePathIndex; pub use self::indexes::MoveOutIndex; +impl self::indexes::MoveOutIndex { + pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex { + move_data.moves[self.idx()].path + } +} + /// `MovePath` is a canonicalized representation of a path that is /// moved or assigned to. /// @@ -125,6 +130,7 @@ impl<'tcx> fmt::Debug for MovePath<'tcx> { } } +#[derive(Debug)] pub struct MoveData<'tcx> { pub move_paths: MovePathData<'tcx>, pub moves: Vec, @@ -133,6 +139,7 @@ pub struct MoveData<'tcx> { pub rev_lookup: MovePathLookup<'tcx>, } +#[derive(Debug)] pub struct LocMap { /// Location-indexed (BasicBlock for outer index, index within BB /// for inner index) map to list of MoveOutIndex's. @@ -153,6 +160,7 @@ impl Index for LocMap { } } +#[derive(Debug)] pub struct PathMap { /// Path-indexed map to list of MoveOutIndex's. /// @@ -187,7 +195,7 @@ impl fmt::Debug for MoveOut { } } -#[derive(Copy, Clone)] +#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)] pub struct Location { /// block where action is located pub block: BasicBlock, @@ -202,10 +210,15 @@ impl fmt::Debug for Location { } } +#[derive(Debug)] pub struct MovePathData<'tcx> { move_paths: Vec>, } +impl<'tcx> MovePathData<'tcx> { + pub fn len(&self) -> usize { self.move_paths.len() } +} + impl<'tcx> Index for MovePathData<'tcx> { type Output = MovePath<'tcx>; fn index(&self, i: MovePathIndex) -> &MovePath<'tcx> { @@ -224,6 +237,7 @@ struct MovePathDataBuilder<'a, 'tcx: 'a> { } /// Tables mapping from an l-value to its MovePathIndex. +#[derive(Debug)] pub struct MovePathLookup<'tcx> { vars: MovePathInverseMap, temps: MovePathInverseMap, @@ -272,6 +286,7 @@ impl FillTo for Vec { #[derive(Clone, Debug)] enum LookupKind { Generate, Reuse } +#[derive(Clone, Debug)] struct Lookup(LookupKind, T); impl Lookup { @@ -425,6 +440,8 @@ impl<'a, 'tcx> MovePathDataBuilder<'a, 'tcx> { } fn move_path_for(&mut self, lval: &Lvalue<'tcx>) -> MovePathIndex { + debug!("move_path_for({:?})", lval); + let lookup: Lookup = self.lookup(lval); // `lookup` is either the previously assigned index or a @@ -547,7 +564,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD debug_assert!(loc_map_bb.len() == len + 1); let mut bb_ctxt = BlockContext { - tcx: tcx, + _tcx: tcx, moves: &mut moves, builder: builder, path_map: &mut path_map, @@ -608,23 +625,17 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD // example where I am seeing this arise is // `TargetDataLayout::parse(&Session)` in // `rustc::ty::layout`. - debug!("encountered Rvalue::Slice as RHS of Assign, source: {:?} \n{}", - source, { - let mut out = Vec::new(); - { - use std::io::Write; - use rustc_mir::pretty::write_mir_named; - let mut w: &mut Write = &mut out; - write_mir_named(tcx, "boo_attempt_move_out_of_slice", mir, &mut w, None).unwrap(); - } - String::from_utf8(out).unwrap() - }); + // + // this should be removed soon. + debug!("encountered Rvalue::Slice as RHS of Assign, source: {:?}", + source); } } } } } + debug!("gather_moves({:?})", bb_data.terminator()); match bb_data.terminator().kind { TerminatorKind::Goto { target: _ } | TerminatorKind::Resume => { } @@ -642,7 +653,6 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD TerminatorKind::If { ref cond, targets: _ } => { let source = Location { block: bb, index: bb_data.statements.len() }; - debug!("gather_moves If on_operand {:?} {:?}", cond, source); bb_ctxt.on_operand(SK::If, cond, source); } @@ -658,10 +668,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD TerminatorKind::Drop { value: ref lval, target: _, unwind: _ } => { let source = Location { block: bb, index: bb_data.statements.len() }; - debug!("gather_moves Drop on_move_out_lval {:?} {:?}", lval, source); bb_ctxt.on_move_out_lval(SK::Drop, lval, source); } - TerminatorKind::Call { ref func, ref args, ref destination, cleanup: _ } => { let source = Location { block: bb, index: bb_data.statements.len() }; @@ -727,7 +735,7 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD } struct BlockContext<'b, 'a: 'b, 'tcx: 'a> { - tcx: TyCtxt<'b, 'tcx, 'tcx>, + _tcx: TyCtxt<'b, 'tcx, 'tcx>, moves: &'b mut Vec, builder: MovePathDataBuilder<'a, 'tcx>, path_map: &'b mut Vec>, @@ -739,7 +747,6 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> { stmt_kind: StmtKind, lval: &Lvalue<'tcx>, source: Location) { - let tcx = self.tcx; let i = source.index; let index = MoveOutIndex::new(self.moves.len()); @@ -774,13 +781,3 @@ impl<'b, 'a: 'b, 'tcx: 'a> BlockContext<'b, 'a, 'tcx> { } } } - -impl<'tcx> BitDenotation for MoveData<'tcx>{ - type Bit = MoveOut; - fn bits_per_block(&self) -> usize { - self.moves.len() - } - fn interpret(&self, idx: usize) -> &Self::Bit { - &self.moves[idx] - } -} diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index bec5ae03d3d4d..44a53a2352869 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -10,24 +10,49 @@ use borrowck::BorrowckCtxt; -use syntax::ast; -use syntax::codemap::Span; +use syntax::ast::{self, MetaItem}; +use syntax::attr::AttrMetaMethods; +use syntax::codemap::{Span, DUMMY_SP}; +use syntax::ptr::P; use rustc::hir; use rustc::hir::intravisit::{FnKind}; +use rustc::mir::repr; use rustc::mir::repr::{BasicBlock, BasicBlockData, Mir, Statement, Terminator}; +use rustc::session::Session; +use rustc::ty::{self, TyCtxt}; mod abs_domain; mod dataflow; mod gather_moves; -mod graphviz; +// mod graphviz; -use self::dataflow::{Dataflow, DataflowState}; -use self::gather_moves::{MoveData}; +use self::dataflow::{BitDenotation}; +use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults}; +use self::dataflow::{HasMoveData}; +use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; +use self::gather_moves::{MoveData, MovePathIndex, Location}; +use self::gather_moves::{MovePathContent}; -pub fn borrowck_mir<'b, 'a: 'b, 'tcx: 'a>( - bcx: &'b mut BorrowckCtxt<'a, 'tcx>, +use std::fmt::Debug; + +fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option> { + for attr in attrs { + if attr.check_name("rustc_mir") { + let items = attr.meta_item_list(); + for item in items.iter().flat_map(|l| l.iter()) { + if item.check_name(name) { + return Some(item.clone()) + } + } + } + } + return None; +} + +pub fn borrowck_mir<'a, 'tcx: 'a>( + bcx: &mut BorrowckCtxt<'a, 'tcx>, fk: FnKind, _decl: &hir::FnDecl, mir: &'a Mir<'tcx>, @@ -45,29 +70,89 @@ pub fn borrowck_mir<'b, 'a: 'b, 'tcx: 'a>( } } + let tcx = bcx.tcx; + + let move_data = MoveData::gather_moves(mir, tcx); + let ctxt = (tcx, mir, move_data); + let (ctxt, flow_inits) = + do_dataflow(bcx, mir, id, attributes, ctxt, MaybeInitializedLvals::default()); + let ((_, _, move_data), flow_uninits) = + do_dataflow(bcx, mir, id, attributes, ctxt, MaybeUninitializedLvals::default()); + let mut mbcx = MirBorrowckCtxt { - flow_state: DataflowState::new_move_analysis(mir, bcx.tcx), bcx: bcx, mir: mir, node_id: id, - attributes: attributes, + move_data: move_data, + flow_inits: flow_inits, + flow_uninits: flow_uninits, }; for bb in mir.all_basic_blocks() { mbcx.process_basic_block(bb); } + debug!("borrowck_mir done"); +} + +fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + node_id: ast::NodeId, + attributes: &[ast::Attribute], + ctxt: BD::Ctxt, + bd: BD) -> (BD::Ctxt, DataflowResults) + where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> +{ + use syntax::attr::AttrMetaMethods; + + let name_found = |sess: &Session, attrs: &[ast::Attribute], name| -> Option { + if let Some(item) = has_rustc_mir_with(attrs, name) { + if let Some(s) = item.value_str() { + return Some(s.to_string()) + } else { + sess.span_err( + item.span, + &format!("{} attribute requires a path", item.name())); + return None; + } + } + return None; + }; + + let print_preflow_to = + name_found(tcx.sess, attributes, "borrowck_graphviz_preflow"); + let print_postflow_to = + name_found(tcx.sess, attributes, "borrowck_graphviz_postflow"); + + let mut mbcx = MirBorrowckCtxtPreDataflow { + node_id: node_id, + print_preflow_to: print_preflow_to, + print_postflow_to: print_postflow_to, + flow_state: DataflowAnalysis::new(tcx, mir, ctxt, bd), + }; + mbcx.dataflow(); + mbcx.flow_state.results() +} - debug!("borrowck_mir done"); + +pub struct MirBorrowckCtxtPreDataflow<'a, 'tcx: 'a, BD> + where BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> +{ + node_id: ast::NodeId, + flow_state: DataflowAnalysis<'a, 'tcx, BD>, + print_preflow_to: Option, + print_postflow_to: Option, } +#[allow(dead_code)] pub struct MirBorrowckCtxt<'b, 'a: 'b, 'tcx: 'a> { bcx: &'b mut BorrowckCtxt<'a, 'tcx>, mir: &'b Mir<'tcx>, node_id: ast::NodeId, - attributes: &'b [ast::Attribute], - flow_state: DataflowState>, + move_data: MoveData<'tcx>, + flow_inits: DataflowResults>, + flow_uninits: DataflowResults> } impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> { @@ -89,3 +174,127 @@ impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> { debug!("MirBorrowckCtxt::process_terminator({:?}, {:?})", bb, term); } } + +#[derive(Debug, PartialEq, Eq, Copy, Clone)] +enum DropFlagState { + Live, + Dead +} + +fn on_all_children_bits<'a, 'tcx, F>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx>, + move_path_index: MovePathIndex, + mut each_child: F) + where F: FnMut(MovePathIndex) +{ + fn is_terminal_path<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx>, + path: MovePathIndex) -> bool + { + match move_data.move_paths[path].content { + MovePathContent::Lvalue(ref lvalue) => { + match mir.lvalue_ty(tcx, lvalue).to_ty(tcx).sty { + // don't trace paths past arrays, slices, and + // pointers. They can only be accessed while + // their parents are initialized. + // + // FIXME: we have to do something for moving + // slice patterns. + ty::TyArray(..) | ty::TySlice(..) | + ty::TyRef(..) | ty::TyRawPtr(..) => true, + _ => false + } + } + _ => true + } + } + + fn on_all_children_bits<'a, 'tcx, F>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx>, + move_path_index: MovePathIndex, + each_child: &mut F) + where F: FnMut(MovePathIndex) + { + each_child(move_path_index); + + if is_terminal_path(tcx, mir, move_data, move_path_index) { + return + } + + let mut next_child_index = move_data.move_paths[move_path_index].first_child; + while let Some(child_index) = next_child_index { + on_all_children_bits(tcx, mir, move_data, child_index, each_child); + next_child_index = move_data.move_paths[child_index].next_sibling; + } + } + on_all_children_bits(tcx, mir, move_data, move_path_index, &mut each_child); +} + +fn drop_flag_effects_for_function_entry<'a, 'tcx, F>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx>, + mut callback: F) + where F: FnMut(MovePathIndex, DropFlagState) +{ + for i in 0..(mir.arg_decls.len() as u32) { + let lvalue = repr::Lvalue::Arg(i); + let move_path_index = move_data.rev_lookup.find(&lvalue); + on_all_children_bits(tcx, mir, move_data, + move_path_index, + |moi| callback(moi, DropFlagState::Live)); + } +} + +fn drop_flag_effects_for_location<'a, 'tcx, F>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + move_data: &MoveData<'tcx>, + loc: Location, + mut callback: F) + where F: FnMut(MovePathIndex, DropFlagState) +{ + debug!("drop_flag_effects_for_location({:?})", loc); + + // first, move out of the RHS + for mi in &move_data.loc_map[loc] { + let path = mi.move_path_index(move_data); + debug!("moving out of path {:?}", move_data.move_paths[path]); + + // don't move out of non-Copy things + if let MovePathContent::Lvalue(ref lvalue) = move_data.move_paths[path].content { + let ty = mir.lvalue_ty(tcx, lvalue).to_ty(tcx); + let empty_param_env = tcx.empty_parameter_environment(); + if !ty.moves_by_default(tcx, &empty_param_env, DUMMY_SP) { + continue; + } + } + + on_all_children_bits(tcx, mir, move_data, + path, + |moi| callback(moi, DropFlagState::Dead)) + } + + let bb = mir.basic_block_data(loc.block); + match bb.statements.get(loc.index) { + Some(stmt) => match stmt.kind { + repr::StatementKind::Assign(ref lvalue, _) => { + debug!("drop_flag_effects: assignment {:?}", stmt); + on_all_children_bits(tcx, mir, move_data, + move_data.rev_lookup.find(lvalue), + |moi| callback(moi, DropFlagState::Live)) + } + }, + None => { + // terminator - no move-ins except for function return edge + let term = bb.terminator(); + debug!("drop_flag_effects: terminator {:?}", term); + } + } +} From 3bb598429afacd0416c0c9bc5d0a4520d17de901 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 11 May 2016 21:54:12 +0200 Subject: [PATCH 12/48] Adding magic `rustc_peek` intrinsic that other code can repurpose to its own needs based on attributes attached to the function where it appears. --- src/libcore/intrinsics.rs | 10 ++++++++++ src/librustc_typeck/check/intrinsic.rs | 1 + 2 files changed, 11 insertions(+) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 8a9f662bf83aa..225929fb350be 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -168,6 +168,16 @@ extern "rust-intrinsic" { pub fn atomic_singlethreadfence_rel(); pub fn atomic_singlethreadfence_acqrel(); + /// Magic intrinsic that derives its meaning from attributes + /// attached to the function. + /// + /// For example, dataflow uses this to inject static assertions so + /// that `rustc_oeek(potentially_uninitialized)` would actually + /// double-check that dataflow did indeed compute that it is + /// uninitialized at that point in the control flow. + #[cfg(not(stage0))] + pub fn rustc_peek(_: T) -> T; + /// Aborts the execution of the process. pub fn abort() -> !; diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index c02139140aed5..f120e38630b8a 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -117,6 +117,7 @@ pub fn check_intrinsic_type(ccx: &CrateCtxt, it: &hir::ForeignItem) { param(ccx, 0)) ], ccx.tcx.types.usize) } + "rustc_peek" => (1, vec![param(ccx, 0)], param(ccx, 0)), "init" | "init_dropped" => (1, Vec::new(), param(ccx, 0)), "uninit" => (1, Vec::new(), param(ccx, 0)), "forget" => (1, vec!( param(ccx, 0) ), tcx.mk_nil()), From 5839e6bb10bbb66c517d5f2571d5ff5677a19bdb Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 11 May 2016 22:03:57 +0200 Subject: [PATCH 13/48] Add ability to unit-test dataflow results via `rustc_peek` intrinsic. (The static semantics of `rustc_peek` is derived from attributes attached to the function being compiled; in this case, `rustc_peek(&expr)` observes the dataflow state for the l-value `expr`.) --- .../borrowck/mir/dataflow/sanity_check.rs | 193 ++++++++++++++++++ src/librustc_borrowck/borrowck/mir/mod.rs | 18 +- 2 files changed, 208 insertions(+), 3 deletions(-) create mode 100644 src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs new file mode 100644 index 0000000000000..f74fb4ebadc29 --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -0,0 +1,193 @@ +// Copyright 2012-2016 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. + +use syntax::abi::{Abi}; +use syntax::ast; + +use rustc::ty::{self, TyCtxt}; +use rustc::mir::repr::{self, Mir}; + +use bitslice::BitSlice; + +use super::super::gather_moves::MovePath; +use super::{bitwise, Union, Subtract}; +use super::BitDenotation; +use super::DataflowResults; +use super::HasMoveData; + +pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + id: ast::NodeId, + _attributes: &[ast::Attribute], + flow_ctxt: &O::Ctxt, + results: &DataflowResults) + where O: BitDenotation>, O::Ctxt: HasMoveData<'tcx> +{ + debug!("sanity_check_via_rustc_peek id: {:?}", id); + // FIXME: this is not DRY. Figure out way to abstract this and + // `dataflow::build_sets`. (But see note below about how the + // behavior of this traversal is a bit different than that + // performed by `build_sets`.) + + let blocks = mir.all_basic_blocks(); + 'next_block: for bb in blocks { + let bb_data = mir.basic_block_data(bb); + let &repr::BasicBlockData { ref statements, + ref terminator, + is_cleanup: _ } = bb_data; + + let (args, span) = if let Some(repr::Terminator { ref kind, span, .. }) = *terminator { + if let repr::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind + { + if let repr::Operand::Constant(ref func) = *oper + { + if let ty::TyFnDef(def_id, _, &ty::BareFnTy { abi, .. }) = func.ty.sty + { + let name = tcx.item_name(def_id); + if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { + if name.as_str() == "rustc_peek" { + (args, span) + } else { + continue; + } + } else { + continue; + } + } else { + continue; + } + } else { + continue; + } + } else { + continue; + } + } else { + continue; + }; + assert!(args.len() == 1); + let peek_arg_lval = match args[0] { + repr::Operand::Consume(ref lval @ repr::Lvalue::Temp(_)) => { + lval + } + repr::Operand::Consume(_) => { + bug!("dataflow::sanity_check cannot feed a non-temp to rustc_peek."); + } + repr::Operand::Constant(_) => { + bug!("dataflow::sanity_check cannot feed a constant to rustc_peek."); + } + }; + + let mut entry = results.0.sets.on_entry_set_for(bb.index()).to_owned(); + let mut gen = results.0.sets.gen_set_for(bb.index()).to_owned(); + let mut kill = results.0.sets.kill_set_for(bb.index()).to_owned(); + + let move_data = flow_ctxt.move_data(); + + // Emulate effect of all statements in the block up to (but + // not including) the assignment to `peek_arg_lval`. Do *not* + // include terminator (since we are peeking the state of the + // argument at time immediate preceding Call to `rustc_peek`). + + let mut sets = super::BlockSets { on_entry: &mut entry[..], + gen_set: &mut gen[..], + kill_set: &mut kill[..] }; + // Unfortunately if we just re-do the same thing that dataflow does, then + // it will always appear like Lvalues are initialized; e.g. in + // a case like: + // + // + // tmp13 = var1; + // tmp14 = &tmp13; + // rustc_peek(tmp14) + // + // The gen_set for normal dataflow would treat tmp13 as + // initialized, even though it's source expression is + // uninitialized. + // + // Work around this for rustc_peek by explicitly propagating + // the relevant bitvector state when computing the effect of a + // statement. + + for (j, stmt) in statements.iter().enumerate() { + debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt); + let (lvalue, rvalue) = match stmt.kind { + repr::StatementKind::Assign(ref lvalue, ref rvalue) => { + (lvalue, rvalue) + } + }; + + if lvalue == peek_arg_lval { + if let repr::Rvalue::Ref(_, + repr::BorrowKind::Shared, + ref peeking_at_lval) = *rvalue { + // Okay, our search is over. + let peek_mpi = move_data.rev_lookup.find(peeking_at_lval); + let bit_state = sets.on_entry.get_bit(peek_mpi.idx()); + debug!("rustc_peek({:?} = &{:?}) bit_state: {}", + lvalue, peeking_at_lval, bit_state); + if !bit_state { + tcx.sess.span_err(span, &format!("rustc_peek: bit not set")); + } + continue 'next_block; + } else { + // Our search should have been over, but the input + // does not match expectations of `rustc_peek` for + // this sanity_check. + tcx.sess.span_err(span, &format!("rustc_peek: argument expression \ + must be immediate borrow of form `&expr`")); + } + } + + enum Effect<'a, 'tcx:'a> { Propagate(&'a repr::Lvalue<'tcx>), Compute } + let lvalue_effect: Effect = match *rvalue { + // tmp = rhs + repr::Rvalue::Use(repr::Operand::Consume(ref rhs_lval)) => + Effect::Propagate(rhs_lval), + + repr::Rvalue::Use(repr::Operand::Constant(_)) => + Effect::Compute, + + _ => { + // (fall back to BitDenotation for all other kinds of Rvalues + Effect::Compute + } + }; + + let lhs_mpi = move_data.rev_lookup.find(lvalue); + + if let Effect::Propagate(rhs_lval) = lvalue_effect { + let rhs_mpi = move_data.rev_lookup.find(rhs_lval); + let state = sets.on_entry.get_bit(rhs_mpi.idx()); + debug!("rustc_peek: propagate into lvalue {:?} ({:?}) from rhs: {:?} state: {}", + lvalue, lhs_mpi, rhs_lval, state); + if state { + sets.on_entry.set_bit(lhs_mpi.idx()); + } else { + sets.on_entry.clear_bit(lhs_mpi.idx()); + } + } else { + debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}", + lvalue, lhs_mpi, stmt); + // reset GEN and KILL sets before emulating their effect. + for e in &mut sets.gen_set[..] { *e = 0; } + for e in &mut sets.kill_set[..] { *e = 0; } + results.0.operator.statement_effect(flow_ctxt, &mut sets, bb, j); + bitwise(sets.on_entry, sets.gen_set, &Union); + bitwise(sets.on_entry, sets.kill_set, &Subtract); + } + } + + tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \ + anticipated pattern; note that \ + rustc_peek expects input of \ + form `&expr`")); + } +} diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 44a53a2352869..ce49e9fc93d61 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -75,9 +75,21 @@ pub fn borrowck_mir<'a, 'tcx: 'a>( let move_data = MoveData::gather_moves(mir, tcx); let ctxt = (tcx, mir, move_data); let (ctxt, flow_inits) = - do_dataflow(bcx, mir, id, attributes, ctxt, MaybeInitializedLvals::default()); - let ((_, _, move_data), flow_uninits) = - do_dataflow(bcx, mir, id, attributes, ctxt, MaybeUninitializedLvals::default()); + do_dataflow(tcx, mir, id, attributes, ctxt, MaybeInitializedLvals::default()); + let (ctxt, flow_uninits) = + do_dataflow(tcx, mir, id, attributes, ctxt, MaybeUninitializedLvals::default()); + + if has_rustc_mir_with(attributes, "rustc_peek_maybe_init").is_some() { + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &ctxt, &flow_inits); + } + if has_rustc_mir_with(attributes, "rustc_peek_maybe_uninit").is_some() { + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &ctxt, &flow_uninits); + } + let move_data = ctxt.2; + + if has_rustc_mir_with(attributes, "stop_after_dataflow").is_some() { + bcx.tcx.sess.fatal("stop_after_dataflow ended compilation"); + } let mut mbcx = MirBorrowckCtxt { bcx: bcx, From 8956789c35a77e774effd7a54182752dbedc321e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 6 May 2016 18:48:48 +0200 Subject: [PATCH 14/48] Little unit tests for MIR dataflow analysis. These use new `rustc_peek` (whose semantics is driven by attribute attached to fn). --- .../borrowck/mir/dataflow/mod.rs | 3 + src/test/compile-fail/mir-dataflow/README.md | 53 +++++++++++++++ src/test/compile-fail/mir-dataflow/inits-1.rs | 65 +++++++++++++++++++ .../compile-fail/mir-dataflow/uninits-1.rs | 63 ++++++++++++++++++ .../compile-fail/mir-dataflow/uninits-2.rs | 36 ++++++++++ 5 files changed, 220 insertions(+) create mode 100644 src/test/compile-fail/mir-dataflow/README.md create mode 100644 src/test/compile-fail/mir-dataflow/inits-1.rs create mode 100644 src/test/compile-fail/mir-dataflow/uninits-1.rs create mode 100644 src/test/compile-fail/mir-dataflow/uninits-2.rs diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 41fe6079526cf..af31d92994460 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -25,7 +25,10 @@ use super::DropFlagState; use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep. +pub use self::sanity_check::sanity_check_via_rustc_peek; + mod graphviz; +mod sanity_check; pub trait Dataflow { fn dataflow(&mut self); diff --git a/src/test/compile-fail/mir-dataflow/README.md b/src/test/compile-fail/mir-dataflow/README.md new file mode 100644 index 0000000000000..b3e0315bb8238 --- /dev/null +++ b/src/test/compile-fail/mir-dataflow/README.md @@ -0,0 +1,53 @@ +This directory contains unit tests for the MIR-based dataflow +analysis. + +These unit tests check the dataflow analysis by embedding calls to a +special `rustc_peek` intrinsic within the code, in tandem with an +attribute `#[rustc_mir(rustc_peek_maybe_init)]` (*). With that +attribute in place, `rustc_peek` calls are a signal to the compiler to +lookup the computed dataflow state for the Lvalue corresponding to the +argument expression being fed to `rustc_peek`. If the dataflow state +for that Lvalue is a 1-bit at that point in the control flow, then no +error is emitted by the compiler at that point; if it is a 0-bit, then +that invocation of `rustc_peek` will emit an error with the message +"rustc_peek: bit not set". + +(*): Or `#[rustc_mir(rustc_peek_maybe_uninit)]`, and perhaps other +variants in the future. + +The end effect is that one can write unit tests for MIR dataflow that +perform simple-queries of the computed dataflow state, and the tests +should be able to be robust in the face of changes to how MIR is +represented or constructed. + +---- + +Sometimes understanding the dataflow results is difficult without +looking at the actual MIR control-flow graph being processed with the +corresponding GEN and KILL sets. + +For a graphviz-rendering with dataflow annotations, add the attribute +`#[rustc_mir(borrowck_graphviz_postflow="/path/to/suffix.dot")]` to +the function in question. (You can change the content of +`"suffix.dot"` to control the filenames used for the output). This +will generate a separate file for each dataflow analysis, adding a +prefix (corresponding to the name of the analysis) to the filename in +each generated output path. + + * For example, the above attribute will currently cause two files to + be generated: `/path/to/maybe_init_suffix.dot` and + `/path/to/maybe_uninit_suffix.dot`. + + * The generated `.dot` file shows both the computed dataflow results + on *entry* to each block, as well as the gen- and kill-sets that + were so-called "transfer functions" summarizing the effect of each + basic block. + + * (In addition to the `borrowck_graphviz_postflow` attribute-key + noted above, there is also `borrowck_graphviz_preflow`; it has the + same interface and generates the same set of files, but it renders + the dataflow state after building the gen- and kill-sets but + *before* running the dataflow analysis itself, so each entry-set is + just the initial default state for that dataflow analysis. This is + less useful for understanding the error message output in these + tests.) diff --git a/src/test/compile-fail/mir-dataflow/inits-1.rs b/src/test/compile-fail/mir-dataflow/inits-1.rs new file mode 100644 index 0000000000000..949688098f622 --- /dev/null +++ b/src/test/compile-fail/mir-dataflow/inits-1.rs @@ -0,0 +1,65 @@ +// Copyright 2015 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. + +// General test of maybe_inits state computed by MIR dataflow. + +#![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] + +use std::intrinsics::rustc_peek; +use std::mem::{drop, replace}; + +struct S(i32); + +#[rustc_mir_borrowck] +#[rustc_mir(rustc_peek_maybe_init,stop_after_dataflow)] +fn foo(test: bool, x: &mut S, y: S, mut z: S) -> S { + let ret; + // `ret` starts off uninitialized, so we get an error report here. + unsafe { rustc_peek(&ret); } //~ ERROR rustc_peek: bit not set + + // All function formal parameters start off initialized. + + unsafe { rustc_peek(&x) }; + unsafe { rustc_peek(&y) }; + unsafe { rustc_peek(&z) }; + + ret = if test { + ::std::mem::replace(x, y) + } else { + z = y; + z + }; + + + // `z` may be initialized here. + unsafe { rustc_peek(&z); } + + // `y` is definitely uninitialized here. + unsafe { rustc_peek(&y); } //~ ERROR rustc_peek: bit not set + + // `x` is still (definitely) initialized (replace above is a reborrow). + unsafe { rustc_peek(&x); } + + ::std::mem::drop(x); + + // `x` is *definitely* uninitialized here + unsafe { rustc_peek(&x); } //~ ERROR rustc_peek: bit not set + + // `ret` is now definitely initialized (via `if` above). + unsafe { rustc_peek(&ret); } + + ret +} + +fn main() { + foo(true, &mut S(13), S(14), S(15)); + foo(false, &mut S(13), S(14), S(15)); +} diff --git a/src/test/compile-fail/mir-dataflow/uninits-1.rs b/src/test/compile-fail/mir-dataflow/uninits-1.rs new file mode 100644 index 0000000000000..c13daae24f35d --- /dev/null +++ b/src/test/compile-fail/mir-dataflow/uninits-1.rs @@ -0,0 +1,63 @@ +// Copyright 2015 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. + +// General test of maybe_uninits state computed by MIR dataflow. + +#![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] + +use std::intrinsics::rustc_peek; +use std::mem::{drop, replace}; + +struct S(i32); + +#[rustc_mir_borrowck] +#[rustc_mir(rustc_peek_maybe_uninit,stop_after_dataflow)] +fn foo(test: bool, x: &mut S, y: S, mut z: S) -> S { + let ret; + // `ret` starts off uninitialized + unsafe { rustc_peek(&ret); } + + // All function formal parameters start off initialized. + + unsafe { rustc_peek(&x) }; //~ ERROR rustc_peek: bit not set + unsafe { rustc_peek(&y) }; //~ ERROR rustc_peek: bit not set + unsafe { rustc_peek(&z) }; //~ ERROR rustc_peek: bit not set + + ret = if test { + ::std::mem::replace(x, y) + } else { + z = y; + z + }; + + // `z` may be uninitialized here. + unsafe { rustc_peek(&z); } + + // `y` is definitely uninitialized here. + unsafe { rustc_peek(&y); } + + // `x` is still (definitely) initialized (replace above is a reborrow). + unsafe { rustc_peek(&x); } //~ ERROR rustc_peek: bit not set + + ::std::mem::drop(x); + + // `x` is *definitely* uninitialized here + unsafe { rustc_peek(&x); } + + // `ret` is now definitely initialized (via `if` above). + unsafe { rustc_peek(&ret); } //~ ERROR rustc_peek: bit not set + + ret +} +fn main() { + foo(true, &mut S(13), S(14), S(15)); + foo(false, &mut S(13), S(14), S(15)); +} diff --git a/src/test/compile-fail/mir-dataflow/uninits-2.rs b/src/test/compile-fail/mir-dataflow/uninits-2.rs new file mode 100644 index 0000000000000..a2869da7eb354 --- /dev/null +++ b/src/test/compile-fail/mir-dataflow/uninits-2.rs @@ -0,0 +1,36 @@ +// Copyright 2015 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. + +// General test of maybe_uninits state computed by MIR dataflow. + +#![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] + +use std::intrinsics::rustc_peek; +use std::mem::{drop, replace}; + +struct S(i32); + +#[rustc_mir_borrowck] +#[rustc_mir(rustc_peek_maybe_uninit,stop_after_dataflow,borrowck_graphviz_postflow="/tmp/uninits-2.dot")] +fn foo(x: &mut S) { + // `x` is initialized here, so maybe-uninit bit is 0. + + unsafe { *rustc_peek(&x) }; //~ ERROR rustc_peek: bit not set + + ::std::mem::drop(x); + + // `x` definitely uninitialized here, so maybe-uninit bit is 1. + unsafe { rustc_peek(&x) }; +} +fn main() { + foo(&mut S(13)); + foo(&mut S(13)); +} From ee44f7ed27f8a83670af166ab886ec44e53dc233 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 13 May 2016 20:44:12 +0200 Subject: [PATCH 15/48] `DefinitelyInitializedLvals` dataflow op (goal: move away from `MaybeUninitializedLvals`) --- .../borrowck/mir/dataflow/mod.rs | 161 ++++++++++++++++-- src/librustc_borrowck/borrowck/mir/mod.rs | 6 + .../compile-fail/mir-dataflow/def-inits-1.rs | 63 +++++++ 3 files changed, 220 insertions(+), 10 deletions(-) create mode 100644 src/test/compile-fail/mir-dataflow/def-inits-1.rs diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index af31d92994460..4cf739396e7fc 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -661,6 +661,53 @@ pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> { phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> } +/// `DefinitelyInitializedLvals` tracks all l-values that are definitely +/// initialized upon reaching a particular point in the control flow +/// for a function. +/// +/// FIXME: Note that once flow-analysis is complete, this should be +/// the set-complement of MaybeUninitializedLvals; thus we can get rid +/// of one or the other of these two. I'm inclined to get rid of +/// MaybeUninitializedLvals, simply because the sets will tend to be +/// smaller in this analysis and thus easier for humans to process +/// when debugging. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // definite-init: +/// // { } +/// let a = S; let b = S; let c; let d; // {a, b } +/// +/// if pred { +/// drop(a); // { b, } +/// b = S; // { b, } +/// +/// } else { +/// drop(b); // {a, } +/// d = S; // {a, d} +/// +/// } // { } +/// +/// c = S; // { c } +/// } +/// ``` +/// +/// To determine whether an l-value *may* be uninitialized at a +/// particular control-flow point, one can take the set-complement +/// of this data. +/// +/// Similarly, at a given `drop` statement, the set-difference between +/// this data and `MaybeInitializedLvals` yields the set of l-values +/// that would require a dynamic drop-flag at that statement. +#[derive(Debug, Default)] +pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> { + // See "Note on PhantomData" above. + phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> +} + /// `MovingOutStatements` tracks the statements that perform moves out /// of particular l-values. More precisely, it tracks whether the /// *effect* of such moves (namely, the uninitialization of the @@ -809,6 +856,23 @@ impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { } } +impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + state: super::DropFlagState) + { + match state { + DropFlagState::Dead => { + sets.gen_set.clear_bit(path.idx()); + sets.kill_set.set_bit(path.idx()); + } + DropFlagState::Live => { + sets.gen_set.set_bit(path.idx()); + sets.kill_set.clear_bit(path.idx()); + } + } + } +} + impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { type Bit = MovePath<'tcx>; type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); @@ -940,6 +1004,72 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { } } +impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { + type Bit = MovePath<'tcx>; + type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + fn name() -> &'static str { "definite_init" } + fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { + ctxt.2.move_paths.len() + } + fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { + &ctxt.2.move_paths[MovePathIndex::new(idx)] + } + + // sets on_entry bits for Arg lvalues + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { + for e in &mut sets.on_entry[..] { *e = 0; } + + super::drop_flag_effects_for_function_entry( + ctxt.0, ctxt.1, &ctxt.2, + |path, s| { + assert!(s == DropFlagState::Live); + sets.on_entry.set_bit(path.idx()); + }); + } + + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx: usize) + { + super::drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: idx }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + statements_len: usize) + { + super::drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: statements_len }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut [usize], + _call_bb: repr::BasicBlock, + _dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue) { + // when a call returns successfully, that means we need to set + // the bits for that dest_lval to 1 (initialized). + let move_path_index = ctxt.2.rev_lookup.find(dest_lval); + super::on_all_children_bits( + ctxt.0, ctxt.1, &ctxt.2, + move_path_index, + |mpi| { in_out.set_bit(mpi.idx()); } + ); + } +} + fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) { let retval = bitvec.set_bit(move_index.idx()); assert!(retval); @@ -966,21 +1096,25 @@ impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> { } } -// FIXME: I'm not sure it ever makes sense to use `true` for a -// DataflowOperator::initial_value implementation, because: the way -// that dataflow fixed point iteration works, you want to start at -// bottom and work your way to a fixed point. +impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 & pred2 // "definitely" means we intersect effects of both preds + } +} + +// FIXME: `DataflowOperator::initial_value` should be named +// `bottom_value`. The way that dataflow fixed point iteration works, +// you want to start at bottom and work your way to a fixed point. +// This needs to include the detail that the control-flow merges will +// apply the `join` operator above to current state (which starts at +// that bottom value). // // This means, for propagation across the graph, that you either want // to start at all-zeroes and then use Union as your merge when // propagating, or you start at all-ones and then use Intersect as // your merge when propagating. -// -// (An alternative could be, when propagating from Block A into block -// B, to clear B's on_entry bits, and then iterate over all of B's -// immediate predecessors. This would require storing on_exit state -// for each block, however.) - + impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> { #[inline] fn initial_value() -> bool { @@ -1002,6 +1136,13 @@ impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> { } } +impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> { + #[inline] + fn initial_value() -> bool { + true // bottom = initialized + } +} + #[inline] fn bitwise(out_vec: &mut [usize], in_vec: &[usize], diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index ce49e9fc93d61..405647aec026e 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -32,6 +32,7 @@ use self::dataflow::{BitDenotation}; use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults}; use self::dataflow::{HasMoveData}; use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; +use self::dataflow::{DefinitelyInitializedLvals}; use self::gather_moves::{MoveData, MovePathIndex, Location}; use self::gather_moves::{MovePathContent}; @@ -78,6 +79,8 @@ pub fn borrowck_mir<'a, 'tcx: 'a>( do_dataflow(tcx, mir, id, attributes, ctxt, MaybeInitializedLvals::default()); let (ctxt, flow_uninits) = do_dataflow(tcx, mir, id, attributes, ctxt, MaybeUninitializedLvals::default()); + let (ctxt, flow_def_inits) = + do_dataflow(tcx, mir, id, attributes, ctxt, DefinitelyInitializedLvals::default()); if has_rustc_mir_with(attributes, "rustc_peek_maybe_init").is_some() { dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &ctxt, &flow_inits); @@ -85,6 +88,9 @@ pub fn borrowck_mir<'a, 'tcx: 'a>( if has_rustc_mir_with(attributes, "rustc_peek_maybe_uninit").is_some() { dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &ctxt, &flow_uninits); } + if has_rustc_mir_with(attributes, "rustc_peek_definite_init").is_some() { + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &ctxt, &flow_def_inits); + } let move_data = ctxt.2; if has_rustc_mir_with(attributes, "stop_after_dataflow").is_some() { diff --git a/src/test/compile-fail/mir-dataflow/def-inits-1.rs b/src/test/compile-fail/mir-dataflow/def-inits-1.rs new file mode 100644 index 0000000000000..a133ddc15f1ac --- /dev/null +++ b/src/test/compile-fail/mir-dataflow/def-inits-1.rs @@ -0,0 +1,63 @@ +// Copyright 2015 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. + +// General test of maybe_uninits state computed by MIR dataflow. + +#![feature(rustc_attrs)] +#![feature(stmt_expr_attributes)] + +use std::intrinsics::rustc_peek; +use std::mem::{drop, replace}; + +struct S(i32); + +#[rustc_mir_borrowck] +#[rustc_mir(rustc_peek_definite_init,stop_after_dataflow)] +fn foo(test: bool, x: &mut S, y: S, mut z: S) -> S { + let ret; + // `ret` starts off uninitialized + unsafe { rustc_peek(&ret); } //~ ERROR rustc_peek: bit not set + + // All function formal parameters start off initialized. + + unsafe { rustc_peek(&x) }; + unsafe { rustc_peek(&y) }; + unsafe { rustc_peek(&z) }; + + ret = if test { + ::std::mem::replace(x, y) + } else { + z = y; + z + }; + + // `z` may be uninitialized here. + unsafe { rustc_peek(&z); } //~ ERROR rustc_peek: bit not set + + // `y` is definitely uninitialized here. + unsafe { rustc_peek(&y); } //~ ERROR rustc_peek: bit not set + + // `x` is still (definitely) initialized (replace above is a reborrow). + unsafe { rustc_peek(&x); } + + ::std::mem::drop(x); + + // `x` is *definitely* uninitialized here + unsafe { rustc_peek(&x); } //~ ERROR rustc_peek: bit not set + + // `ret` is now definitely initialized (via `if` above). + unsafe { rustc_peek(&ret); } + + ret +} +fn main() { + foo(true, &mut S(13), S(14), S(15)); + foo(false, &mut S(13), S(14), S(15)); +} From cd71b0dd54d7852d6f57c42ea5251a2e600b134f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 16 May 2016 17:10:44 +0200 Subject: [PATCH 16/48] core::intrinsics: fix typo noted during review. --- src/libcore/intrinsics.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 225929fb350be..0350824ee359d 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -172,7 +172,7 @@ extern "rust-intrinsic" { /// attached to the function. /// /// For example, dataflow uses this to inject static assertions so - /// that `rustc_oeek(potentially_uninitialized)` would actually + /// that `rustc_peek(potentially_uninitialized)` would actually /// double-check that dataflow did indeed compute that it is /// uninitialized at that point in the control flow. #[cfg(not(stage0))] From b0b1f4da60ae452d8155d84015696bb89232da73 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 17 May 2016 08:37:36 +0200 Subject: [PATCH 17/48] Fix comments in `mir::dataflow::sanity_check`. --- .../borrowck/mir/dataflow/sanity_check.rs | 37 +++++++++---------- 1 file changed, 18 insertions(+), 19 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index f74fb4ebadc29..39eb0c33dfbc7 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -22,6 +22,22 @@ use super::BitDenotation; use super::DataflowResults; use super::HasMoveData; +/// This function scans `mir` for all calls to the intrinsic +/// `rustc_peek` that have the expression form `rustc_peek(&expr)`. +/// +/// For each such call, determines what the dataflow bit-state is for +/// the L-value corresponding to `expr`; if the bit-state is a 1, then +/// that call to `rustc_peek` is ignored by the sanity check. If the +/// bit-state is a 0, then this pass emits a error message saying +/// "rustc_peek: bit not set". +/// +/// The intention is that one can write unit tests for dataflow by +/// putting code into a compile-fail test and using `rustc_peek` to +/// make observations about the results of dataflow static analyses. +/// +/// (If there are any calls to `rustc_peek` that do not match the +/// expression form above, then that emits an error as well, but those +/// errors are not intended to be used for unit tests.) pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, id: ast::NodeId, @@ -32,9 +48,8 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, { debug!("sanity_check_via_rustc_peek id: {:?}", id); // FIXME: this is not DRY. Figure out way to abstract this and - // `dataflow::build_sets`. (But see note below about how the - // behavior of this traversal is a bit different than that - // performed by `build_sets`.) + // `dataflow::build_sets`. (But note it is doing non-standard + // stuff, so such generalization may not be realistic.) let blocks = mir.all_basic_blocks(); 'next_block: for bb in blocks { @@ -99,22 +114,6 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut sets = super::BlockSets { on_entry: &mut entry[..], gen_set: &mut gen[..], kill_set: &mut kill[..] }; - // Unfortunately if we just re-do the same thing that dataflow does, then - // it will always appear like Lvalues are initialized; e.g. in - // a case like: - // - // - // tmp13 = var1; - // tmp14 = &tmp13; - // rustc_peek(tmp14) - // - // The gen_set for normal dataflow would treat tmp13 as - // initialized, even though it's source expression is - // uninitialized. - // - // Work around this for rustc_peek by explicitly propagating - // the relevant bitvector state when computing the effect of a - // statement. for (j, stmt) in statements.iter().enumerate() { debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt); From 90a652617b23f56e4a89db822b464de341e61049 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 17 May 2016 08:44:24 +0200 Subject: [PATCH 18/48] Escape asterix in markdown file to side-step it being interpreted as emphasis. --- src/test/compile-fail/mir-dataflow/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/mir-dataflow/README.md b/src/test/compile-fail/mir-dataflow/README.md index b3e0315bb8238..eaf2cf7f80598 100644 --- a/src/test/compile-fail/mir-dataflow/README.md +++ b/src/test/compile-fail/mir-dataflow/README.md @@ -3,7 +3,7 @@ analysis. These unit tests check the dataflow analysis by embedding calls to a special `rustc_peek` intrinsic within the code, in tandem with an -attribute `#[rustc_mir(rustc_peek_maybe_init)]` (*). With that +attribute `#[rustc_mir(rustc_peek_maybe_init)]` (\*). With that attribute in place, `rustc_peek` calls are a signal to the compiler to lookup the computed dataflow state for the Lvalue corresponding to the argument expression being fed to `rustc_peek`. If the dataflow state From 8999e877ed860961a6ca3c1150e5641ba6eb336f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 17 May 2016 09:06:18 +0200 Subject: [PATCH 19/48] `mir::dataflow::sanity_check`: removed hackish `tmp = val` propagation code. (it was an artifact of an earlier design of the `rustc_peek` API, but its totally unnecessary now.) --- .../borrowck/mir/dataflow/sanity_check.rs | 43 ++++--------------- 1 file changed, 8 insertions(+), 35 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index 39eb0c33dfbc7..b45b79229128a 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -145,43 +145,16 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } - enum Effect<'a, 'tcx:'a> { Propagate(&'a repr::Lvalue<'tcx>), Compute } - let lvalue_effect: Effect = match *rvalue { - // tmp = rhs - repr::Rvalue::Use(repr::Operand::Consume(ref rhs_lval)) => - Effect::Propagate(rhs_lval), - - repr::Rvalue::Use(repr::Operand::Constant(_)) => - Effect::Compute, - - _ => { - // (fall back to BitDenotation for all other kinds of Rvalues - Effect::Compute - } - }; - let lhs_mpi = move_data.rev_lookup.find(lvalue); - if let Effect::Propagate(rhs_lval) = lvalue_effect { - let rhs_mpi = move_data.rev_lookup.find(rhs_lval); - let state = sets.on_entry.get_bit(rhs_mpi.idx()); - debug!("rustc_peek: propagate into lvalue {:?} ({:?}) from rhs: {:?} state: {}", - lvalue, lhs_mpi, rhs_lval, state); - if state { - sets.on_entry.set_bit(lhs_mpi.idx()); - } else { - sets.on_entry.clear_bit(lhs_mpi.idx()); - } - } else { - debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}", - lvalue, lhs_mpi, stmt); - // reset GEN and KILL sets before emulating their effect. - for e in &mut sets.gen_set[..] { *e = 0; } - for e in &mut sets.kill_set[..] { *e = 0; } - results.0.operator.statement_effect(flow_ctxt, &mut sets, bb, j); - bitwise(sets.on_entry, sets.gen_set, &Union); - bitwise(sets.on_entry, sets.kill_set, &Subtract); - } + debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}", + lvalue, lhs_mpi, stmt); + // reset GEN and KILL sets before emulating their effect. + for e in &mut sets.gen_set[..] { *e = 0; } + for e in &mut sets.kill_set[..] { *e = 0; } + results.0.operator.statement_effect(flow_ctxt, &mut sets, bb, j); + bitwise(sets.on_entry, sets.gen_set, &Union); + bitwise(sets.on_entry, sets.kill_set, &Subtract); } tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \ From 582f060a17d4b4a4e007db25548d01670bbdc496 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 13:12:02 +0200 Subject: [PATCH 20/48] markdown fix suggested during review. --- src/test/compile-fail/mir-dataflow/README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/mir-dataflow/README.md b/src/test/compile-fail/mir-dataflow/README.md index eaf2cf7f80598..a3ab14b23c7db 100644 --- a/src/test/compile-fail/mir-dataflow/README.md +++ b/src/test/compile-fail/mir-dataflow/README.md @@ -12,7 +12,7 @@ error is emitted by the compiler at that point; if it is a 0-bit, then that invocation of `rustc_peek` will emit an error with the message "rustc_peek: bit not set". -(*): Or `#[rustc_mir(rustc_peek_maybe_uninit)]`, and perhaps other +(\*): Or `#[rustc_mir(rustc_peek_maybe_uninit)]`, and perhaps other variants in the future. The end effect is that one can write unit tests for MIR dataflow that From aaad8a209a239fdd9bb9d018768ecce66d83d1f5 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 13:18:03 +0200 Subject: [PATCH 21/48] `mir::dataflow::sanity_check`: Factor out `fn is_rustc_peek` helper routine. --- .../borrowck/mir/dataflow/sanity_check.rs | 56 +++++++++---------- 1 file changed, 28 insertions(+), 28 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index b45b79229128a..7c8a9a4aeb0f4 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -10,6 +10,7 @@ use syntax::abi::{Abi}; use syntax::ast; +use syntax::codemap::Span; use rustc::ty::{self, TyCtxt}; use rustc::mir::repr::{self, Mir}; @@ -58,34 +59,9 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ref terminator, is_cleanup: _ } = bb_data; - let (args, span) = if let Some(repr::Terminator { ref kind, span, .. }) = *terminator { - if let repr::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind - { - if let repr::Operand::Constant(ref func) = *oper - { - if let ty::TyFnDef(def_id, _, &ty::BareFnTy { abi, .. }) = func.ty.sty - { - let name = tcx.item_name(def_id); - if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { - if name.as_str() == "rustc_peek" { - (args, span) - } else { - continue; - } - } else { - continue; - } - } else { - continue; - } - } else { - continue; - } - } else { - continue; - } - } else { - continue; + let (args, span) = match is_rustc_peek(tcx, terminator) { + Some(args_and_span) => args_and_span, + None => continue, }; assert!(args.len() == 1); let peek_arg_lval = match args[0] { @@ -162,4 +138,28 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, rustc_peek expects input of \ form `&expr`")); } + +} + +fn is_rustc_peek<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + terminator: &'a Option>) + -> Option<(&'a [repr::Operand<'tcx>], Span)> { + if let Some(repr::Terminator { ref kind, span, .. }) = *terminator { + if let repr::TerminatorKind::Call { func: ref oper, ref args, .. } = *kind + { + if let repr::Operand::Constant(ref func) = *oper + { + if let ty::TyFnDef(def_id, _, &ty::BareFnTy { abi, .. }) = func.ty.sty + { + let name = tcx.item_name(def_id); + if abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { + if name.as_str() == "rustc_peek" { + return Some((args, span)); + } + } + } + } + } + } + return None; } From 9fcbe2a2f902d6a538a3605877e0eb1b3ad9314f Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 13:18:27 +0200 Subject: [PATCH 22/48] fix comment in `impl DataflowOperator for MaybeUninitializedLvals`. --- src/librustc_borrowck/borrowck/mir/dataflow/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 4cf739396e7fc..9f0186a36a839 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -1132,7 +1132,7 @@ impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> { #[inline] fn initial_value() -> bool { - false // bottom = uninitialized + false // bottom = initialized (start_block_effect counters this at outset) } } From 011c37d59e51ba6553b9146aeb32a9a4cf5a179d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 13:20:00 +0200 Subject: [PATCH 23/48] `borrowck::mir`: alpha-renamed DropFlagState variant names. --- .../borrowck/mir/dataflow/mod.rs | 18 +++++++++--------- src/librustc_borrowck/borrowck/mir/mod.rs | 10 +++++----- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 9f0186a36a839..cb110e606f227 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -827,11 +827,11 @@ impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { state: super::DropFlagState) { match state { - DropFlagState::Dead => { + DropFlagState::Absent => { sets.gen_set.clear_bit(path.idx()); sets.kill_set.set_bit(path.idx()); } - DropFlagState::Live => { + DropFlagState::Present => { sets.gen_set.set_bit(path.idx()); sets.kill_set.clear_bit(path.idx()); } @@ -844,11 +844,11 @@ impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { state: super::DropFlagState) { match state { - DropFlagState::Dead => { + DropFlagState::Absent => { sets.gen_set.set_bit(path.idx()); sets.kill_set.clear_bit(path.idx()); } - DropFlagState::Live => { + DropFlagState::Present => { sets.gen_set.clear_bit(path.idx()); sets.kill_set.set_bit(path.idx()); } @@ -861,11 +861,11 @@ impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { state: super::DropFlagState) { match state { - DropFlagState::Dead => { + DropFlagState::Absent => { sets.gen_set.clear_bit(path.idx()); sets.kill_set.set_bit(path.idx()); } - DropFlagState::Live => { + DropFlagState::Present => { sets.gen_set.set_bit(path.idx()); sets.kill_set.clear_bit(path.idx()); } @@ -889,7 +889,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { super::drop_flag_effects_for_function_entry( ctxt.0, ctxt.1, &ctxt.2, |path, s| { - assert!(s == DropFlagState::Live); + assert!(s == DropFlagState::Present); sets.on_entry.set_bit(path.idx()); }); } @@ -956,7 +956,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { super::drop_flag_effects_for_function_entry( ctxt.0, ctxt.1, &ctxt.2, |path, s| { - assert!(s == DropFlagState::Live); + assert!(s == DropFlagState::Present); sets.on_entry.clear_bit(path.idx()); }); } @@ -1022,7 +1022,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { super::drop_flag_effects_for_function_entry( ctxt.0, ctxt.1, &ctxt.2, |path, s| { - assert!(s == DropFlagState::Live); + assert!(s == DropFlagState::Present); sets.on_entry.set_bit(path.idx()); }); } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 405647aec026e..a3cf6176779c3 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -195,8 +195,8 @@ impl<'b, 'a: 'b, 'tcx: 'a> MirBorrowckCtxt<'b, 'a, 'tcx> { #[derive(Debug, PartialEq, Eq, Copy, Clone)] enum DropFlagState { - Live, - Dead + Present, // i.e. initialized + Absent, // i.e. deinitialized or "moved" } fn on_all_children_bits<'a, 'tcx, F>( @@ -266,7 +266,7 @@ fn drop_flag_effects_for_function_entry<'a, 'tcx, F>( let move_path_index = move_data.rev_lookup.find(&lvalue); on_all_children_bits(tcx, mir, move_data, move_path_index, - |moi| callback(moi, DropFlagState::Live)); + |moi| callback(moi, DropFlagState::Present)); } } @@ -296,7 +296,7 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( on_all_children_bits(tcx, mir, move_data, path, - |moi| callback(moi, DropFlagState::Dead)) + |moi| callback(moi, DropFlagState::Absent)) } let bb = mir.basic_block_data(loc.block); @@ -306,7 +306,7 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( debug!("drop_flag_effects: assignment {:?}", stmt); on_all_children_bits(tcx, mir, move_data, move_data.rev_lookup.find(lvalue), - |moi| callback(moi, DropFlagState::Live)) + |moi| callback(moi, DropFlagState::Present)) } }, None => { From 9c468f4b65961ec1d3ce3f993c59d250afde00be Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 13:20:33 +0200 Subject: [PATCH 24/48] Added comment pointing out somewhat subtle initialization in `fn start_block_effect`. --- src/librustc_borrowck/borrowck/mir/dataflow/mod.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index cb110e606f227..4bc5a5409ece5 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -951,6 +951,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { // sets on_entry bits for Arg lvalues fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { + // set all bits to 1 (uninit) before gathering counterevidence for e in &mut sets.on_entry[..] { *e = !0; } super::drop_flag_effects_for_function_entry( From a7e3204ac8a1ed282f94fb688bdf53b851e5cbab Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 13:40:52 +0200 Subject: [PATCH 25/48] `mir::dataflow` arielb1 review feedback * removed `on_all_children_bits`, rewriting calls to use `super::on_all_children_bits` * moved `fn path` helper routine out of `impl MirBorrowckCtxtPreDataflow` --- .../borrowck/mir/dataflow/mod.rs | 96 +++++++------------ 1 file changed, 32 insertions(+), 64 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 4bc5a5409ece5..8c3a843e46479 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -19,7 +19,7 @@ use std::path::PathBuf; use std::usize; use super::MirBorrowckCtxtPreDataflow; -use super::gather_moves::{Location, MoveData, MovePathData, MovePathIndex, MoveOutIndex, PathMap}; +use super::gather_moves::{Location, MoveData, MovePathIndex, MoveOutIndex}; use super::gather_moves::{MoveOut, MovePath}; use super::DropFlagState; @@ -99,40 +99,6 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> } } -fn on_all_children_bits(path_map: &PathMap, - move_paths: &MovePathData, - move_path_index: MovePathIndex, - mut each_child: Each) - where Each: FnMut(MoveOutIndex) -{ - return on_all_children_bits_recur( - path_map, move_paths, move_path_index, &mut each_child); - - fn on_all_children_bits_recur(path_map: &PathMap, - move_paths: &MovePathData, - move_path_index: MovePathIndex, - each_child: &mut Each) - where Each: FnMut(MoveOutIndex) - { - // 1. invoke `each_child` callback for all moves that directly - // influence path for `move_path_index` - for move_index in &path_map[move_path_index] { - each_child(*move_index); - } - - // 2. for each child of the path (that is named in this - // function), recur. - // - // (Unnamed children are irrelevant to dataflow; by - // definition they have no associated moves.) - let mut next_child_index = move_paths[move_path_index].first_child; - while let Some(child_index) = next_child_index { - on_all_children_bits_recur(path_map, move_paths, child_index, each_child); - next_child_index = move_paths[child_index].next_sibling; - } - } -} - impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> where BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> { @@ -161,23 +127,23 @@ impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> } } +fn dataflow_path(context: &str, prepost: &str, path: &str) -> PathBuf { + format!("{}_{}", context, prepost); + let mut path = PathBuf::from(path); + let new_file_name = { + let orig_file_name = path.file_name().unwrap().to_str().unwrap(); + format!("{}_{}", context, orig_file_name) + }; + path.set_file_name(new_file_name); + path +} + impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> { - fn path(context: &str, prepost: &str, path: &str) -> PathBuf { - format!("{}_{}", context, prepost); - let mut path = PathBuf::from(path); - let new_file_name = { - let orig_file_name = path.file_name().unwrap().to_str().unwrap(); - format!("{}_{}", context, orig_file_name) - }; - path.set_file_name(new_file_name); - path - } - fn pre_dataflow_instrumentation(&self) -> io::Result<()> { if let Some(ref path_str) = self.print_preflow_to { - let path = Self::path(BD::name(), "preflow", path_str); + let path = dataflow_path(BD::name(), "preflow", path_str); graphviz::print_borrowck_graph_to(self, &path) } else { Ok(()) @@ -186,7 +152,7 @@ impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> fn post_dataflow_instrumentation(&self) -> io::Result<()> { if let Some(ref path_str) = self.print_postflow_to { - let path = Self::path(BD::name(), "postflow", path_str); + let path = dataflow_path(BD::name(), "postflow", path_str); graphviz::print_borrowck_graph_to(self, &path) } else{ Ok(()) @@ -746,9 +712,8 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { sets: &mut BlockSets, bb: repr::BasicBlock, idx: usize) { - let &(_tcx, mir, ref move_data) = ctxt; + let &(tcx, mir, ref move_data) = ctxt; let stmt = &mir.basic_block_data(bb).statements[idx]; - let move_paths = &move_data.move_paths; let loc_map = &move_data.loc_map; let path_map = &move_data.path_map; let rev_lookup = &move_data.rev_lookup; @@ -771,13 +736,14 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { let move_path_index = rev_lookup.find(lvalue); sets.kill_set.set_bit(move_path_index.idx()); - on_all_children_bits(path_map, - move_paths, - move_path_index, - |moi| { - assert!(moi.idx() < bits_per_block); - sets.kill_set.set_bit(moi.idx()); - }); + super::on_all_children_bits(tcx, + mir, + move_data, + move_path_index, + |mpi| for moi in &path_map[mpi] { + assert!(moi.idx() < bits_per_block); + sets.kill_set.set_bit(moi.idx()); + }); } } } @@ -812,13 +778,15 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { let bits_per_block = self.bits_per_block(ctxt); in_out.clear_bit(move_path_index.idx()); - on_all_children_bits(&move_data.path_map, - &move_data.move_paths, - move_path_index, - |moi| { - assert!(moi.idx() < bits_per_block); - in_out.clear_bit(moi.idx()); - }); + let path_map = &move_data.path_map; + super::on_all_children_bits(ctxt.0, + ctxt.1, + move_data, + move_path_index, + |mpi| for moi in &path_map[mpi] { + assert!(moi.idx() < bits_per_block); + in_out.clear_bit(moi.idx()); + }); } } From 581195090a6c1609aa03abfabdb20e11f3b833ed Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 14:14:18 +0200 Subject: [PATCH 26/48] Review feedback. Removed `BitDenotation: DataflowOperator` relationship. Alpha-renamed `fn initial_value` to `fn bottom_value`. --- .../borrowck/mir/dataflow/mod.rs | 41 +++++++++---------- src/librustc_borrowck/borrowck/mir/mod.rs | 3 +- 2 files changed, 21 insertions(+), 23 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 8c3a843e46479..00589e05ffd3d 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -35,7 +35,7 @@ pub trait Dataflow { } impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation + DataflowOperator, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> { fn dataflow(&mut self) { self.flow_state.build_sets(); @@ -53,7 +53,7 @@ struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> } impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> - where BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation + DataflowOperator, BD::Ctxt: HasMoveData<'tcx> { fn propagate(&mut self) { let mut temp = vec![0; self.flow_state.sets.words_per_block]; @@ -100,10 +100,10 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> } impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> - where BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation + DataflowOperator, BD::Ctxt: HasMoveData<'tcx> { fn reset(&mut self, bits: &mut [usize]) { - let e = if BD::initial_value() {usize::MAX} else {0}; + let e = if BD::bottom_value() {usize::MAX} else {0}; for b in bits { *b = e; } @@ -317,17 +317,17 @@ impl DataflowState { } pub trait BitwiseOperator { - /// Joins two predecessor bits together, typically either `|` or `&` + /// Applies some bit-operation pointwise to each of the bits in the two inputs. fn join(&self, pred1: usize, pred2: usize) -> usize; } /// Parameterization for the precise form of data flow that is used. -pub trait DataflowOperator : BitwiseOperator { +pub trait DataflowOperator: BitwiseOperator { /// Specifies the initial value for each bit in the `on_entry` set - fn initial_value() -> bool; + fn bottom_value() -> bool; } -pub trait BitDenotation: DataflowOperator { +pub trait BitDenotation { /// Specifies what is represented by each bit in the dataflow bitvector. type Bit; @@ -425,7 +425,7 @@ pub trait BitDenotation: DataflowOperator { } impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> - where D: BitDenotation, D::Ctxt: HasMoveData<'tcx> + where D: BitDenotation + DataflowOperator, D::Ctxt: HasMoveData<'tcx> { pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, @@ -437,7 +437,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> let num_blocks = mir.basic_blocks.len(); let num_words = num_blocks * words_per_block; - let entry = if D::initial_value() { usize::MAX } else {0}; + let entry = if D::bottom_value() { usize::MAX } else {0}; let zeroes = Bits::new(0, num_words); let on_entry = Bits::new(entry, num_words); @@ -460,7 +460,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> } impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> - where D: BitDenotation, D::Ctxt: HasMoveData<'tcx> + where D: BitDenotation + DataflowOperator, D::Ctxt: HasMoveData<'tcx> { /// Propagates the bits of `in_out` into all the successors of `bb`, /// using bitwise operator denoted by `self.operator`. @@ -851,7 +851,6 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { &ctxt.2.move_paths[MovePathIndex::new(idx)] } - fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { super::drop_flag_effects_for_function_entry( @@ -1072,12 +1071,10 @@ impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { } } -// FIXME: `DataflowOperator::initial_value` should be named -// `bottom_value`. The way that dataflow fixed point iteration works, -// you want to start at bottom and work your way to a fixed point. -// This needs to include the detail that the control-flow merges will -// apply the `join` operator above to current state (which starts at -// that bottom value). +// The way that dataflow fixed point iteration works, you want to +// start at bottom and work your way to a fixed point. Control-flow +// merges will apply the `join` operator to each block entry's current +// state (which starts at that bottom value). // // This means, for propagation across the graph, that you either want // to start at all-zeroes and then use Union as your merge when @@ -1086,28 +1083,28 @@ impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> { #[inline] - fn initial_value() -> bool { + fn bottom_value() -> bool { false // bottom = no loans in scope by default } } impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> { #[inline] - fn initial_value() -> bool { + fn bottom_value() -> bool { false // bottom = uninitialized } } impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> { #[inline] - fn initial_value() -> bool { + fn bottom_value() -> bool { false // bottom = initialized (start_block_effect counters this at outset) } } impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> { #[inline] - fn initial_value() -> bool { + fn bottom_value() -> bool { true // bottom = initialized } } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index a3cf6176779c3..37c042844e58b 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -29,6 +29,7 @@ mod gather_moves; // mod graphviz; use self::dataflow::{BitDenotation}; +use self::dataflow::{DataflowOperator}; use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults}; use self::dataflow::{HasMoveData}; use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; @@ -119,7 +120,7 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, attributes: &[ast::Attribute], ctxt: BD::Ctxt, bd: BD) -> (BD::Ctxt, DataflowResults) - where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation + DataflowOperator, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> { use syntax::attr::AttrMetaMethods; From 59008cbd71e0d8725d314d60b90ca9f66ee69064 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 15:44:30 +0200 Subject: [PATCH 27/48] review feedback: fix some index-mismatch bugs pointed out by arielb1. --- src/librustc_borrowck/borrowck/mir/dataflow/mod.rs | 3 --- 1 file changed, 3 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 00589e05ffd3d..09abbb6af681f 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -734,8 +734,6 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { // MoveOuts from it, and *also* all MoveOuts // for children and associated fragment sets. let move_path_index = rev_lookup.find(lvalue); - - sets.kill_set.set_bit(move_path_index.idx()); super::on_all_children_bits(tcx, mir, move_data, @@ -777,7 +775,6 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { let move_path_index = move_data.rev_lookup.find(dest_lval); let bits_per_block = self.bits_per_block(ctxt); - in_out.clear_bit(move_path_index.idx()); let path_map = &move_data.path_map; super::on_all_children_bits(ctxt.0, ctxt.1, From f18bafdbe1eaa2b0c918debbabfaafeb3614e69d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Fri, 20 May 2016 17:40:22 +0200 Subject: [PATCH 28/48] Refactor `bitslice`: distinguish `usize` for indexing vs word type being indexed. --- src/librustc_borrowck/bitslice.rs | 24 +++++++++++++----------- 1 file changed, 13 insertions(+), 11 deletions(-) diff --git a/src/librustc_borrowck/bitslice.rs b/src/librustc_borrowck/bitslice.rs index ca672e808843b..1c01e98cf859c 100644 --- a/src/librustc_borrowck/bitslice.rs +++ b/src/librustc_borrowck/bitslice.rs @@ -10,7 +10,9 @@ use std::mem; -/// `BitSlice` provides helper methods for treating a `[usize]` +pub type Word = usize; + +/// `BitSlice` provides helper methods for treating a `[Word]` /// as a bitvector. pub trait BitSlice { fn clear_bit(&mut self, idx: usize) -> bool; @@ -18,12 +20,12 @@ pub trait BitSlice { fn get_bit(&self, idx: usize) -> bool; } -impl BitSlice for [usize] { +impl BitSlice for [Word] { /// Clears bit at `idx` to 0; returns true iff this changed `self.` fn clear_bit(&mut self, idx: usize) -> bool { let words = self; debug!("clear_bit: words={} idx={}", - bits_to_string(words, words.len() * mem::size_of::()), bit_str(idx)); + bits_to_string(words, words.len() * mem::size_of::()), bit_str(idx)); let BitLookup { word, bit_in_word, bit_mask } = bit_lookup(idx); debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask); let oldv = words[word]; @@ -36,7 +38,7 @@ impl BitSlice for [usize] { fn set_bit(&mut self, idx: usize) -> bool { let words = self; debug!("set_bit: words={} idx={}", - bits_to_string(words, words.len() * mem::size_of::()), bit_str(idx)); + bits_to_string(words, words.len() * mem::size_of::()), bit_str(idx)); let BitLookup { word, bit_in_word, bit_mask } = bit_lookup(idx); debug!("word={} bit_in_word={} bit_mask={}", word, bit_in_word, bit_mask); let oldv = words[word]; @@ -54,31 +56,31 @@ impl BitSlice for [usize] { } struct BitLookup { - /// An index of the word holding the bit in original `[usize]` of query. + /// An index of the word holding the bit in original `[Word]` of query. word: usize, /// Index of the particular bit within the word holding the bit. bit_in_word: usize, /// Word with single 1-bit set corresponding to where the bit is located. - bit_mask: usize, + bit_mask: Word, } #[inline] fn bit_lookup(bit: usize) -> BitLookup { - let usize_bits = mem::size_of::() * 8; - let word = bit / usize_bits; - let bit_in_word = bit % usize_bits; + let word_bits = mem::size_of::() * 8; + let word = bit / word_bits; + let bit_in_word = bit % word_bits; let bit_mask = 1 << bit_in_word; BitLookup { word: word, bit_in_word: bit_in_word, bit_mask: bit_mask } } -fn bit_str(bit: usize) -> String { +fn bit_str(bit: Word) -> String { let byte = bit >> 3; let lobits = 1 << (bit & 0b111); format!("[{}:{}-{:02x}]", bit, byte, lobits) } -pub fn bits_to_string(words: &[usize], bits: usize) -> String { +pub fn bits_to_string(words: &[Word], bits: usize) -> String { let mut result = String::new(); let mut sep = '['; From 0796ee77baf4a186413c1176878e6705fa9dc377 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 23 May 2016 14:31:52 +0200 Subject: [PATCH 29/48] add `indexed_set` mod for typed wrappers around bitarrays representing sets. It provides an `Idx` trait for usize wrappers used to represent the elements of such sets. --- src/librustc_borrowck/indexed_set.rs | 87 ++++++++++++++++++++++++++++ src/librustc_borrowck/lib.rs | 1 + 2 files changed, 88 insertions(+) create mode 100644 src/librustc_borrowck/indexed_set.rs diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs new file mode 100644 index 0000000000000..c743e58aa9960 --- /dev/null +++ b/src/librustc_borrowck/indexed_set.rs @@ -0,0 +1,87 @@ +// Copyright 2012-2016 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. + +use std::fmt; +use std::marker::PhantomData; +use std::mem; +use bitslice::{BitSlice, Word}; + +pub trait Indexed { + type Idx: Idx; +} + +pub trait Idx { + fn idx(&self) -> usize; +} + +pub struct OwnIdxSet { + _pd: PhantomData &T>, + bits: Vec, +} + +// pnkfelix wants to have this be `IdxSet([Word]) and then pass +// around `&mut IdxSet` or `&IdxSet`. +// +// Mmapping a `&OwnIdxSet` to `&IdxSet` (at least today) +// requires a transmute relying on representation guarantees that may +// not hold in the future. + +pub struct IdxSet { + _pd: PhantomData &T>, + bits: [Word], +} + +impl fmt::Debug for OwnIdxSet { + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) } +} + +impl fmt::Debug for IdxSet { + fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) } +} + +impl OwnIdxSet { + fn new(init: Word, universe_size: usize) -> Self { + let bits_per_word = mem::size_of::(); + let num_words = (universe_size + (bits_per_word - 1)) / bits_per_word; + OwnIdxSet { + _pd: Default::default(), + bits: vec![init; num_words], + } + } + + /// Creates set holding every element whose index falls in range 0..universe_size. + pub fn new_filled(universe_size: usize) -> Self { + Self::new(!0, universe_size) + } + + /// Creates set holding no elements. + pub fn new_empty(universe_size: usize) -> Self { + Self::new(0, universe_size) + } + + /// Removes `elem` from the set `self`; returns true iff this changed `self`. + pub fn clear(&mut self, elem: &T) -> bool { + self.bits.clear_bit(elem.idx()) + } + + /// Adds `elem` to the set `self`; returns true iff this changed `self`. + pub fn add(&mut self, elem: &T) -> bool { + self.bits.set_bit(elem.idx()) + } + + /// Returns true iff set `self` contains `elem`. + pub fn contains(&self, elem: &T) -> bool { + self.bits.get_bit(elem.idx()) + } + + pub fn bits(&self) -> &[Word] { + &self.bits[..] + } +} diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index e38677de6625f..9d7e05ed9fa86 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -47,6 +47,7 @@ pub mod diagnostics; mod borrowck; mod bitslice; +mod indexed_set; pub mod graphviz; From 71af40bb9dd5887ce9797e73f608b48b0f41355b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Mon, 23 May 2016 18:26:52 +0200 Subject: [PATCH 30/48] revised mir-dataflow so bitvectors carry a phantom type for their index domain. As some drive-by's: * moved bitwise operators into `mod bitslice` * factored out `fn gen` and `fn kill` methods on `BlockSets` type * removed outdated comment about `fn propagate_call_return` --- src/librustc_borrowck/bitslice.rs | 29 ++ .../borrowck/mir/dataflow/graphviz.rs | 11 +- .../borrowck/mir/dataflow/mod.rs | 297 +++++++++--------- .../borrowck/mir/dataflow/sanity_check.rs | 23 +- .../borrowck/mir/gather_moves.rs | 17 +- src/librustc_borrowck/indexed_set.rs | 75 ++++- 6 files changed, 272 insertions(+), 180 deletions(-) diff --git a/src/librustc_borrowck/bitslice.rs b/src/librustc_borrowck/bitslice.rs index 1c01e98cf859c..7a203b7f0b716 100644 --- a/src/librustc_borrowck/bitslice.rs +++ b/src/librustc_borrowck/bitslice.rs @@ -109,3 +109,32 @@ pub fn bits_to_string(words: &[Word], bits: usize) -> String { result.push(']'); return result } + +#[inline] +pub fn bitwise(out_vec: &mut [usize], + in_vec: &[usize], + op: &Op) -> bool { + assert_eq!(out_vec.len(), in_vec.len()); + let mut changed = false; + for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) { + let old_val = *out_elt; + let new_val = op.join(old_val, *in_elt); + *out_elt = new_val; + changed |= old_val != new_val; + } + changed +} + +pub trait BitwiseOperator { + /// Applies some bit-operation pointwise to each of the bits in the two inputs. + fn join(&self, pred1: usize, pred2: usize) -> usize; +} + +pub struct Union; +impl BitwiseOperator for Union { + fn join(&self, a: usize, b: usize) -> usize { a | b } +} +pub struct Subtract; +impl BitwiseOperator for Subtract { + fn join(&self, a: usize, b: usize) -> usize { a & !b } +} diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs index bc6ee89fa2577..5a8b3ec32062c 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs @@ -54,8 +54,9 @@ struct Graph<'a, 'tcx, MWF:'a> where MWF: MirWithFlowState<'tcx>, pub fn print_borrowck_graph_to<'a, 'tcx, BD>( mbcx: &MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>, - path: &Path) -> io::Result<()> where BD: BitDenotation, - BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> + path: &Path) + -> io::Result<()> + where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx>, { let g = Graph { mbcx: mbcx, phantom: PhantomData }; let mut v = Vec::new(); @@ -180,7 +181,7 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> ", bg = BG_FLOWCONTENT, face = FACE_MONOSPACE, - entrybits=bits_to_string(entry, bits_per_block)) + entrybits=bits_to_string(entry.words(), bits_per_block)) }, |w| { let ctxt = self.mbcx.analysis_ctxt(); @@ -197,7 +198,7 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> ", bg = BG_FLOWCONTENT, face = FACE_MONOSPACE, - genbits=bits_to_string(gen, bits_per_block))?; + genbits=bits_to_string(gen.words(), bits_per_block))?; } { @@ -209,7 +210,7 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> bg = BG_FLOWCONTENT, align = ALIGN_RIGHT, face = FACE_MONOSPACE, - killbits=bits_to_string(kill, bits_per_block))?; + killbits=bits_to_string(kill.words(), bits_per_block))?; } // (chunked_present_right) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 09abbb6af681f..e1459ad2abeae 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -24,6 +24,8 @@ use super::gather_moves::{MoveOut, MovePath}; use super::DropFlagState; use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep. +use bitslice::{bitwise, BitwiseOperator}; +use indexed_set::{Idx, IdxSet, OwnIdxSet}; pub use self::sanity_check::sanity_check_via_rustc_peek; @@ -35,7 +37,9 @@ pub trait Dataflow { } impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where BD: BitDenotation + DataflowOperator, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation + DataflowOperator, + BD::Bit: Debug, + BD::Ctxt: HasMoveData<'tcx> { fn dataflow(&mut self) { self.flow_state.build_sets(); @@ -46,7 +50,7 @@ impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> } struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> - where O: 'b + BitDenotation, O::Ctxt: HasMoveData<'tcx>, + where O: 'b + BitDenotation, O::Ctxt: HasMoveData<'tcx> { builder: &'b mut DataflowAnalysis<'a, 'tcx, O>, changed: bool, @@ -56,7 +60,7 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation + DataflowOperator, BD::Ctxt: HasMoveData<'tcx> { fn propagate(&mut self) { - let mut temp = vec![0; self.flow_state.sets.words_per_block]; + let mut temp = OwnIdxSet::new_empty(self.flow_state.sets.bits_per_block); let mut propcx = PropagationContext { builder: self, changed: true, @@ -102,23 +106,23 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> where BD: BitDenotation + DataflowOperator, BD::Ctxt: HasMoveData<'tcx> { - fn reset(&mut self, bits: &mut [usize]) { - let e = if BD::bottom_value() {usize::MAX} else {0}; - for b in bits { + fn reset(&mut self, bits: &mut IdxSet) { + let e = if BD::bottom_value() {!0} else {0}; + for b in bits.words_mut() { *b = e; } } - fn walk_cfg(&mut self, in_out: &mut [usize]) { + fn walk_cfg(&mut self, in_out: &mut IdxSet) { let mir = self.builder.mir; for (bb_idx, bb_data) in mir.basic_blocks.iter().enumerate() { let builder = &mut self.builder; { let sets = builder.flow_state.sets.for_block(bb_idx); - debug_assert!(in_out.len() == sets.on_entry.len()); - in_out.clone_from_slice(sets.on_entry); - bitwise(in_out, sets.gen_set, &Union); - bitwise(in_out, sets.kill_set, &Subtract); + debug_assert!(in_out.words().len() == sets.on_entry.words().len()); + in_out.clone_from(sets.on_entry); + in_out.union(sets.gen_set); + in_out.subtract(sets.kill_set); } builder.propagate_bits_into_graph_successors_of(in_out, &mut self.changed, @@ -161,14 +165,18 @@ impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> } /// Maps each block to a set of bits -#[derive(Clone, Debug)] -struct Bits { - bits: Vec, +#[derive(Debug)] +struct Bits { + bits: OwnIdxSet, +} + +impl Clone for Bits { + fn clone(&self) -> Self { Bits { bits: self.bits.clone() } } } -impl Bits { - fn new(init_word: usize, num_words: usize) -> Self { - Bits { bits: vec![init_word; num_words] } +impl Bits { + fn new(bits: OwnIdxSet) -> Self { + Bits { bits: bits } } } @@ -201,25 +209,23 @@ impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> pub fn mir(&self) -> &'a Mir<'tcx> { self.mir } } -#[derive(Debug)] -pub struct DataflowResults(DataflowState); +pub struct DataflowResults(DataflowState) where O: BitDenotation; // 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. -#[derive(Debug)] pub struct DataflowState { /// All the sets for the analysis. (Factored into its /// own structure so that we can borrow it mutably /// on its own separate from other fields.) - pub sets: AllSets, + pub sets: AllSets, /// operator used to initialize, combine, and interpret bits. operator: O, } #[derive(Debug)] -pub struct AllSets { +pub struct AllSets { /// Analysis bitwidth for each block. bits_per_block: usize, @@ -230,59 +236,71 @@ pub struct AllSets { /// For each block, bits generated by executing the statements in /// the block. (For comparison, the Terminator for each block is /// handled in a flow-specific manner during propagation.) - gen_sets: Bits, + gen_sets: Bits, /// For each block, bits killed by executing the statements in the /// block. (For comparison, the Terminator for each block is /// handled in a flow-specific manner during propagation.) - kill_sets: Bits, + kill_sets: Bits, /// For each block, bits valid on entry to the block. - on_entry_sets: Bits, + on_entry_sets: Bits, } -pub struct BlockSets<'a> { - on_entry: &'a mut [usize], - gen_set: &'a mut [usize], - kill_set: &'a mut [usize], +pub struct BlockSets<'a, E: Idx> { + on_entry: &'a mut IdxSet, + gen_set: &'a mut IdxSet, + kill_set: &'a mut IdxSet, } -impl AllSets { +impl<'a, E:Idx> BlockSets<'a, E> { + fn gen(&mut self, e: &E) { + self.gen_set.add(e); + self.kill_set.remove(e); + } + fn kill(&mut self, e: &E) { + self.gen_set.remove(e); + self.kill_set.add(e); + } +} + +impl AllSets { pub fn bits_per_block(&self) -> usize { self.bits_per_block } - pub fn for_block(&mut self, block_idx: usize) -> BlockSets { + pub fn for_block(&mut self, block_idx: usize) -> BlockSets { let offset = self.words_per_block * block_idx; - let range = offset..(offset + self.words_per_block); + let range = E::new(offset)..E::new(offset + self.words_per_block); BlockSets { - on_entry: &mut self.on_entry_sets.bits[range.clone()], - gen_set: &mut self.gen_sets.bits[range.clone()], - kill_set: &mut self.kill_sets.bits[range], + on_entry: self.on_entry_sets.bits.range_mut(&range), + gen_set: self.gen_sets.bits.range_mut(&range), + kill_set: self.kill_sets.bits.range_mut(&range), } } - fn lookup_set_for<'a>(&self, sets: &'a Bits, block_idx: usize) -> &'a [usize] { + fn lookup_set_for<'a>(&self, sets: &'a Bits, block_idx: usize) -> &'a IdxSet { let offset = self.words_per_block * block_idx; - &sets.bits[offset..(offset + self.words_per_block)] + let range = E::new(offset)..E::new(offset + self.words_per_block); + sets.bits.range(&range) } - pub fn gen_set_for(&self, block_idx: usize) -> &[usize] { + pub fn gen_set_for(&self, block_idx: usize) -> &IdxSet { self.lookup_set_for(&self.gen_sets, block_idx) } - pub fn kill_set_for(&self, block_idx: usize) -> &[usize] { + pub fn kill_set_for(&self, block_idx: usize) -> &IdxSet { self.lookup_set_for(&self.kill_sets, block_idx) } - pub fn on_entry_set_for(&self, block_idx: usize) -> &[usize] { + pub fn on_entry_set_for(&self, block_idx: usize) -> &IdxSet { self.lookup_set_for(&self.on_entry_sets, block_idx) } } impl DataflowState { - fn each_bit(&self, ctxt: &O::Ctxt, words: &[usize], mut f: F) + fn each_bit(&self, ctxt: &O::Ctxt, words: &IdxSet, mut f: F) where F: FnMut(usize) { //! Helper for iterating over the bits in a bitvector. let bits_per_block = self.operator.bits_per_block(ctxt); let usize_bits: usize = mem::size_of::() * 8; - for (word_index, &word) in words.iter().enumerate() { + for (word_index, &word) in words.words().iter().enumerate() { if word != 0 { let base_index = word_index * usize_bits; for offset in 0..usize_bits { @@ -307,7 +325,7 @@ impl DataflowState { } } - pub fn interpret_set<'c>(&self, ctxt: &'c O::Ctxt, words: &[usize]) -> Vec<&'c O::Bit> { + pub fn interpret_set<'c>(&self, ctxt: &'c O::Ctxt, words: &IdxSet) -> Vec<&'c O::Bit> { let mut v = Vec::new(); self.each_bit(ctxt, words, |i| { v.push(self.operator.interpret(ctxt, i)); @@ -316,11 +334,6 @@ impl DataflowState { } } -pub trait BitwiseOperator { - /// Applies some bit-operation pointwise to each of the bits in the two inputs. - fn join(&self, pred1: usize, pred2: usize) -> usize; -} - /// Parameterization for the precise form of data flow that is used. pub trait DataflowOperator: BitwiseOperator { /// Specifies the initial value for each bit in the `on_entry` set @@ -331,6 +344,9 @@ pub trait BitDenotation { /// Specifies what is represented by each bit in the dataflow bitvector. type Bit; + /// Specifies what index type is used to access the bitvector. + type Idx: Idx; + /// Specifies what, if any, separate context needs to be supplied for methods below. type Ctxt; @@ -359,7 +375,7 @@ pub trait BitDenotation { /// (Typically this should only modify `sets.on_entry`, since the /// gen and kill sets should reflect the effects of *executing* /// the start block itself.) - fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets); + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets); /// Mutates the block-sets (the flow sets for the given /// basic block) according to the effects of evaluating statement. @@ -373,7 +389,7 @@ pub trait BitDenotation { /// in the basic block. fn statement_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, idx_stmt: usize); @@ -393,7 +409,7 @@ pub trait BitDenotation { /// terminator took. fn terminator_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, idx_term: usize); @@ -411,21 +427,17 @@ pub trait BitDenotation { /// flow-dependent, the current MIR cannot encode them via just /// GEN and KILL sets attached to the block, and so instead we add /// this extra machinery to represent the flow-dependent effect. - /// - /// Note: as a historical artifact, this currently takes as input - /// the *entire* packed collection of bitvectors in `in_out`. We - /// might want to look into narrowing that to something more - /// specific, just to make the interface more self-documenting. fn propagate_call_return(&self, ctxt: &Self::Ctxt, - in_out: &mut [usize], + in_out: &mut IdxSet, call_bb: repr::BasicBlock, dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue); } impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> - where D: BitDenotation + DataflowOperator, D::Ctxt: HasMoveData<'tcx> + where D: BitDenotation + DataflowOperator, + D::Ctxt: HasMoveData<'tcx> { pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, @@ -434,33 +446,41 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> let bits_per_block = denotation.bits_per_block(&ctxt); let usize_bits = mem::size_of::() * 8; let words_per_block = (bits_per_block + usize_bits - 1) / usize_bits; - let num_blocks = mir.basic_blocks.len(); - let num_words = num_blocks * words_per_block; - let entry = if D::bottom_value() { usize::MAX } else {0}; + // (now rounded up to multiple of word size) + let bits_per_block = words_per_block * usize_bits; - let zeroes = Bits::new(0, num_words); - let on_entry = Bits::new(entry, num_words); + let num_blocks = mir.basic_blocks.len(); + let num_overall = num_blocks * bits_per_block; - DataflowAnalysis { flow_state: DataflowState { - sets: AllSets { - bits_per_block: bits_per_block, - words_per_block: words_per_block, - gen_sets: zeroes.clone(), - kill_sets: zeroes, - on_entry_sets: on_entry, + let zeroes = Bits::new(OwnIdxSet::new_empty(num_overall)); + let on_entry = Bits::new(if D::bottom_value() { + OwnIdxSet::new_filled(num_overall) + } else { + OwnIdxSet::new_empty(num_overall) + }); + + DataflowAnalysis { + ctxt: ctxt, + mir: mir, + flow_state: DataflowState { + sets: AllSets { + bits_per_block: bits_per_block, + words_per_block: words_per_block, + gen_sets: zeroes.clone(), + kill_sets: zeroes, + on_entry_sets: on_entry, + }, + operator: denotation, }, - operator: denotation, - }, - ctxt: ctxt, - mir: mir, } } } impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> - where D: BitDenotation + DataflowOperator, D::Ctxt: HasMoveData<'tcx> + where D: BitDenotation + DataflowOperator, + D::Ctxt: HasMoveData<'tcx>, { /// Propagates the bits of `in_out` into all the successors of `bb`, /// using bitwise operator denoted by `self.operator`. @@ -477,7 +497,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> /// unwind target). fn propagate_bits_into_graph_successors_of( &mut self, - in_out: &mut [usize], + in_out: &mut IdxSet, changed: &mut bool, (bb, bb_data): (repr::BasicBlock, &repr::BasicBlockData)) { @@ -518,11 +538,13 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> } fn propagate_bits_into_entry_set_for(&mut self, - in_out: &[usize], + in_out: &IdxSet, changed: &mut bool, bb: &repr::BasicBlock) { let entry_set = self.flow_state.sets.for_block(bb.index()).on_entry; - let set_changed = bitwise(entry_set, in_out, &self.flow_state.operator); + let set_changed = bitwise(entry_set.words_mut(), + in_out.words(), + &self.flow_state.operator); if set_changed { *changed = true; } @@ -694,6 +716,7 @@ pub struct MovingOutStatements<'a, 'tcx: 'a> { } impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { + type Idx = MoveOutIndex; type Bit = MoveOut; type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); fn name() -> &'static str { "moving_out" } @@ -703,13 +726,13 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { &ctxt.2.moves[idx] } - fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) { + fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) { // no move-statements have been executed prior to function // execution, so this method has no effect on `_sets`. } fn statement_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, idx: usize) { let &(tcx, mir, ref move_data) = ctxt; @@ -725,7 +748,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { // Every path deinitialized by a *particular move* // has corresponding bit, "gen'ed" (i.e. set) // here, in dataflow vector - zero_to_one(&mut sets.gen_set, *move_index); + zero_to_one(sets.gen_set.words_mut(), *move_index); } let bits_per_block = self.bits_per_block(ctxt); match stmt.kind { @@ -740,7 +763,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { move_path_index, |mpi| for moi in &path_map[mpi] { assert!(moi.idx() < bits_per_block); - sets.kill_set.set_bit(moi.idx()); + sets.kill_set.add(&moi); }); } } @@ -748,7 +771,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { fn terminator_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, statements_len: usize) { @@ -761,13 +784,13 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { let bits_per_block = self.bits_per_block(ctxt); for move_index in &loc_map[loc] { assert!(move_index.idx() < bits_per_block); - zero_to_one(&mut sets.gen_set, *move_index); + zero_to_one(sets.gen_set.words_mut(), *move_index); } } fn propagate_call_return(&self, ctxt: &Self::Ctxt, - in_out: &mut [usize], + in_out: &mut IdxSet, _call_bb: repr::BasicBlock, _dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue) { @@ -782,63 +805,46 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { move_path_index, |mpi| for moi in &path_map[mpi] { assert!(moi.idx() < bits_per_block); - in_out.clear_bit(moi.idx()); + in_out.remove(&moi); }); } } impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { - fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: super::DropFlagState) { match state { - DropFlagState::Absent => { - sets.gen_set.clear_bit(path.idx()); - sets.kill_set.set_bit(path.idx()); - } - DropFlagState::Present => { - sets.gen_set.set_bit(path.idx()); - sets.kill_set.clear_bit(path.idx()); - } + DropFlagState::Absent => sets.kill(&path), + DropFlagState::Present => sets.gen(&path), } } } impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { - fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: super::DropFlagState) { match state { - DropFlagState::Absent => { - sets.gen_set.set_bit(path.idx()); - sets.kill_set.clear_bit(path.idx()); - } - DropFlagState::Present => { - sets.gen_set.clear_bit(path.idx()); - sets.kill_set.set_bit(path.idx()); - } + DropFlagState::Absent => sets.gen(&path), + DropFlagState::Present => sets.kill(&path), } } } impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { - fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, state: super::DropFlagState) { match state { - DropFlagState::Absent => { - sets.gen_set.clear_bit(path.idx()); - sets.kill_set.set_bit(path.idx()); - } - DropFlagState::Present => { - sets.gen_set.set_bit(path.idx()); - sets.kill_set.clear_bit(path.idx()); - } + DropFlagState::Absent => sets.kill(&path), + DropFlagState::Present => sets.gen(&path), } } } impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { + type Idx = MovePathIndex; type Bit = MovePath<'tcx>; type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); fn name() -> &'static str { "maybe_init" } @@ -848,19 +854,19 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { &ctxt.2.move_paths[MovePathIndex::new(idx)] } - fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { super::drop_flag_effects_for_function_entry( ctxt.0, ctxt.1, &ctxt.2, |path, s| { assert!(s == DropFlagState::Present); - sets.on_entry.set_bit(path.idx()); + sets.on_entry.add(&path); }); } fn statement_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, idx: usize) { @@ -873,7 +879,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { fn terminator_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, statements_len: usize) { @@ -886,7 +892,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { fn propagate_call_return(&self, ctxt: &Self::Ctxt, - in_out: &mut [usize], + in_out: &mut IdxSet, _call_bb: repr::BasicBlock, _dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue) { @@ -897,12 +903,13 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { super::on_all_children_bits( ctxt.0, ctxt.1, &ctxt.2, move_path_index, - |mpi| { in_out.set_bit(mpi.idx()); } + |mpi| { in_out.add(&mpi); } ); } } impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { + type Idx = MovePathIndex; type Bit = MovePath<'tcx>; type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); fn name() -> &'static str { "maybe_uninit" } @@ -914,21 +921,21 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { } // sets on_entry bits for Arg lvalues - fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { // set all bits to 1 (uninit) before gathering counterevidence - for e in &mut sets.on_entry[..] { *e = !0; } + for e in sets.on_entry.words_mut() { *e = !0; } super::drop_flag_effects_for_function_entry( ctxt.0, ctxt.1, &ctxt.2, |path, s| { assert!(s == DropFlagState::Present); - sets.on_entry.clear_bit(path.idx()); + sets.on_entry.remove(&path); }); } fn statement_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, idx: usize) { @@ -941,7 +948,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { fn terminator_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, statements_len: usize) { @@ -954,7 +961,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { fn propagate_call_return(&self, ctxt: &Self::Ctxt, - in_out: &mut [usize], + in_out: &mut IdxSet, _call_bb: repr::BasicBlock, _dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue) { @@ -964,12 +971,13 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { super::on_all_children_bits( ctxt.0, ctxt.1, &ctxt.2, move_path_index, - |mpi| { in_out.clear_bit(mpi.idx()); } + |mpi| { in_out.remove(&mpi); } ); } } impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { + type Idx = MovePathIndex; type Bit = MovePath<'tcx>; type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); fn name() -> &'static str { "definite_init" } @@ -981,20 +989,20 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { } // sets on_entry bits for Arg lvalues - fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { - for e in &mut sets.on_entry[..] { *e = 0; } + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { + for e in sets.on_entry.words_mut() { *e = 0; } super::drop_flag_effects_for_function_entry( ctxt.0, ctxt.1, &ctxt.2, |path, s| { assert!(s == DropFlagState::Present); - sets.on_entry.set_bit(path.idx()); + sets.on_entry.add(&path); }); } fn statement_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, idx: usize) { @@ -1007,7 +1015,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { fn terminator_effect(&self, ctxt: &Self::Ctxt, - sets: &mut BlockSets, + sets: &mut BlockSets, bb: repr::BasicBlock, statements_len: usize) { @@ -1020,7 +1028,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { fn propagate_call_return(&self, ctxt: &Self::Ctxt, - in_out: &mut [usize], + in_out: &mut IdxSet, _call_bb: repr::BasicBlock, _dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue) { @@ -1030,7 +1038,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { super::on_all_children_bits( ctxt.0, ctxt.1, &ctxt.2, move_path_index, - |mpi| { in_out.set_bit(mpi.idx()); } + |mpi| { in_out.add(&mpi); } ); } } @@ -1106,26 +1114,3 @@ impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> { } } -#[inline] -fn bitwise(out_vec: &mut [usize], - in_vec: &[usize], - op: &Op) -> bool { - assert_eq!(out_vec.len(), in_vec.len()); - let mut changed = false; - for (out_elt, in_elt) in out_vec.iter_mut().zip(in_vec) { - let old_val = *out_elt; - let new_val = op.join(old_val, *in_elt); - *out_elt = new_val; - changed |= old_val != new_val; - } - changed -} - -struct Union; -impl BitwiseOperator for Union { - fn join(&self, a: usize, b: usize) -> usize { a | b } -} -struct Subtract; -impl BitwiseOperator for Subtract { - fn join(&self, a: usize, b: usize) -> usize { a & !b } -} diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index 7c8a9a4aeb0f4..6cf20def0e0c4 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -15,10 +15,7 @@ use syntax::codemap::Span; use rustc::ty::{self, TyCtxt}; use rustc::mir::repr::{self, Mir}; -use bitslice::BitSlice; - -use super::super::gather_moves::MovePath; -use super::{bitwise, Union, Subtract}; +use super::super::gather_moves::{MovePath, MovePathIndex}; use super::BitDenotation; use super::DataflowResults; use super::HasMoveData; @@ -45,7 +42,7 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, _attributes: &[ast::Attribute], flow_ctxt: &O::Ctxt, results: &DataflowResults) - where O: BitDenotation>, O::Ctxt: HasMoveData<'tcx> + where O: BitDenotation, Idx=MovePathIndex>, O::Ctxt: HasMoveData<'tcx> { debug!("sanity_check_via_rustc_peek id: {:?}", id); // FIXME: this is not DRY. Figure out way to abstract this and @@ -87,9 +84,9 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // include terminator (since we are peeking the state of the // argument at time immediate preceding Call to `rustc_peek`). - let mut sets = super::BlockSets { on_entry: &mut entry[..], - gen_set: &mut gen[..], - kill_set: &mut kill[..] }; + let mut sets = super::BlockSets { on_entry: &mut entry, + gen_set: &mut gen, + kill_set: &mut kill }; for (j, stmt) in statements.iter().enumerate() { debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt); @@ -105,7 +102,7 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ref peeking_at_lval) = *rvalue { // Okay, our search is over. let peek_mpi = move_data.rev_lookup.find(peeking_at_lval); - let bit_state = sets.on_entry.get_bit(peek_mpi.idx()); + let bit_state = sets.on_entry.contains(&peek_mpi); debug!("rustc_peek({:?} = &{:?}) bit_state: {}", lvalue, peeking_at_lval, bit_state); if !bit_state { @@ -126,11 +123,11 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}", lvalue, lhs_mpi, stmt); // reset GEN and KILL sets before emulating their effect. - for e in &mut sets.gen_set[..] { *e = 0; } - for e in &mut sets.kill_set[..] { *e = 0; } + for e in sets.gen_set.words_mut() { *e = 0; } + for e in sets.kill_set.words_mut() { *e = 0; } results.0.operator.statement_effect(flow_ctxt, &mut sets, bb, j); - bitwise(sets.on_entry, sets.gen_set, &Union); - bitwise(sets.on_entry, sets.kill_set, &Subtract); + sets.on_entry.union(sets.gen_set); + sets.on_entry.subtract(sets.kill_set); } tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \ diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 519e9398436b8..e196de46ee671 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -20,6 +20,7 @@ use std::iter; use std::ops::Index; use super::abs_domain::{AbstractElem, Lift}; +use indexed_set::{Idx, Indexed}; // This submodule holds some newtype'd Index wrappers that are using // NonZero to ensure that Option occupies only a single word. @@ -28,6 +29,7 @@ use super::abs_domain::{AbstractElem, Lift}; // (which is likely to yield a subtle off-by-one error). mod indexes { use core::nonzero::NonZero; + use indexed_set::Idx; macro_rules! new_index { ($Index:ident) => { @@ -35,10 +37,13 @@ mod indexes { pub struct $Index(NonZero); impl $Index { - pub fn new(idx: usize) -> Self { + } + + impl Idx for $Index { + fn new(idx: usize) -> Self { unsafe { $Index(NonZero::new(idx + 1)) } } - pub fn idx(&self) -> usize { + fn idx(&self) -> usize { *self.0 - 1 } } @@ -55,6 +60,14 @@ mod indexes { pub use self::indexes::MovePathIndex; pub use self::indexes::MoveOutIndex; +impl<'tcx> Indexed for MovePath<'tcx> { + type Idx = MovePathIndex; +} + +impl Indexed for MoveOut { + type Idx = MoveOutIndex; +} + impl self::indexes::MoveOutIndex { pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex { move_data.moves[self.idx()].path diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs index c743e58aa9960..c37f8e09e0ac2 100644 --- a/src/librustc_borrowck/indexed_set.rs +++ b/src/librustc_borrowck/indexed_set.rs @@ -11,13 +11,16 @@ use std::fmt; use std::marker::PhantomData; use std::mem; +use std::ops::{Deref, DerefMut, Range}; use bitslice::{BitSlice, Word}; +use bitslice::{bitwise, Union, Subtract}; pub trait Indexed { type Idx: Idx; } -pub trait Idx { +pub trait Idx: 'static { + fn new(usize) -> Self; fn idx(&self) -> usize; } @@ -26,10 +29,16 @@ pub struct OwnIdxSet { bits: Vec, } +impl Clone for OwnIdxSet { + fn clone(&self) -> Self { + OwnIdxSet { _pd: PhantomData, bits: self.bits.clone() } + } +} + // pnkfelix wants to have this be `IdxSet([Word]) and then pass // around `&mut IdxSet` or `&IdxSet`. // -// Mmapping a `&OwnIdxSet` to `&IdxSet` (at least today) +// WARNING: Mapping a `&OwnIdxSet` to `&IdxSet` (at least today) // requires a transmute relying on representation guarantees that may // not hold in the future. @@ -65,9 +74,41 @@ impl OwnIdxSet { pub fn new_empty(universe_size: usize) -> Self { Self::new(0, universe_size) } +} + +impl IdxSet { + unsafe fn from_slice(s: &[Word]) -> &Self { + mem::transmute(s) // (see above WARNING) + } + + unsafe fn from_slice_mut(s: &mut [Word]) -> &mut Self { + mem::transmute(s) // (see above WARNING) + } +} + +impl Deref for OwnIdxSet { + type Target = IdxSet; + fn deref(&self) -> &IdxSet { + unsafe { IdxSet::from_slice(&self.bits[..]) } + } +} + +impl DerefMut for OwnIdxSet { + fn deref_mut(&mut self) -> &mut IdxSet { + unsafe { IdxSet::from_slice_mut(&mut self.bits[..]) } + } +} + +impl IdxSet { + pub fn to_owned(&self) -> OwnIdxSet { + OwnIdxSet { + _pd: Default::default(), + bits: self.bits.to_owned(), + } + } /// Removes `elem` from the set `self`; returns true iff this changed `self`. - pub fn clear(&mut self, elem: &T) -> bool { + pub fn remove(&mut self, elem: &T) -> bool { self.bits.clear_bit(elem.idx()) } @@ -76,12 +117,38 @@ impl OwnIdxSet { self.bits.set_bit(elem.idx()) } + pub fn range(&self, elems: &Range) -> &Self { + let elems = elems.start.idx()..elems.end.idx(); + unsafe { Self::from_slice(&self.bits[elems]) } + } + + pub fn range_mut(&mut self, elems: &Range) -> &mut Self { + let elems = elems.start.idx()..elems.end.idx(); + unsafe { Self::from_slice_mut(&mut self.bits[elems]) } + } + /// Returns true iff set `self` contains `elem`. pub fn contains(&self, elem: &T) -> bool { self.bits.get_bit(elem.idx()) } - pub fn bits(&self) -> &[Word] { + pub fn words(&self) -> &[Word] { &self.bits[..] } + + pub fn words_mut(&mut self) -> &mut [Word] { + &mut self.bits[..] + } + + pub fn clone_from(&mut self, other: &IdxSet) { + self.words_mut().clone_from_slice(other.words()); + } + + pub fn union(&mut self, other: &IdxSet) -> bool { + bitwise(self.words_mut(), other.words(), &Union) + } + + pub fn subtract(&mut self, other: &IdxSet) -> bool { + bitwise(self.words_mut(), other.words(), &Subtract) + } } From c48650d14e2a64c8966972f4f8bc9559a11a11be Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 12:35:35 +0200 Subject: [PATCH 31/48] bug fix to `borrowck::indexed_set`: wanted bit-count not byte-count. --- src/librustc_borrowck/indexed_set.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs index c37f8e09e0ac2..bf5eb10b8ed53 100644 --- a/src/librustc_borrowck/indexed_set.rs +++ b/src/librustc_borrowck/indexed_set.rs @@ -57,7 +57,7 @@ impl fmt::Debug for IdxSet { impl OwnIdxSet { fn new(init: Word, universe_size: usize) -> Self { - let bits_per_word = mem::size_of::(); + let bits_per_word = mem::size_of::() * 8; let num_words = (universe_size + (bits_per_word - 1)) / bits_per_word; OwnIdxSet { _pd: Default::default(), From b8c6d1c70889d07b39076b025885ce4c00de4e8c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 12:37:39 +0200 Subject: [PATCH 32/48] Fix comment within sanity_check. --- .../borrowck/mir/dataflow/sanity_check.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index 6cf20def0e0c4..3151195a5ad04 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -80,9 +80,10 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let move_data = flow_ctxt.move_data(); // Emulate effect of all statements in the block up to (but - // not including) the assignment to `peek_arg_lval`. Do *not* - // include terminator (since we are peeking the state of the - // argument at time immediate preceding Call to `rustc_peek`). + // not including) the borrow within `peek_arg_lval`. Do *not* + // include call to `peek_arg_lval` itself (since we are + // peeking the state of the argument at time immediate + // preceding Call to `rustc_peek`). let mut sets = super::BlockSets { on_entry: &mut entry, gen_set: &mut gen, From ede29581d2eb856dfae5c37deee396235f3cf6a0 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 12:56:02 +0200 Subject: [PATCH 33/48] `mir::dataflow::sanity_check`: extract an `fn each_block` to simplify presentation. As a drive-by: unified pair of match arms that flowed to `bug!`, and replaced `bug!` invocation with a diagnostic `span_err` invocation. --- .../borrowck/mir/dataflow/sanity_check.rs | 162 ++++++++++-------- 1 file changed, 86 insertions(+), 76 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index 3151195a5ad04..932975b88084b 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -51,92 +51,102 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let blocks = mir.all_basic_blocks(); 'next_block: for bb in blocks { - let bb_data = mir.basic_block_data(bb); - let &repr::BasicBlockData { ref statements, - ref terminator, - is_cleanup: _ } = bb_data; - - let (args, span) = match is_rustc_peek(tcx, terminator) { - Some(args_and_span) => args_and_span, - None => continue, - }; - assert!(args.len() == 1); - let peek_arg_lval = match args[0] { - repr::Operand::Consume(ref lval @ repr::Lvalue::Temp(_)) => { - lval - } - repr::Operand::Consume(_) => { - bug!("dataflow::sanity_check cannot feed a non-temp to rustc_peek."); - } - repr::Operand::Constant(_) => { - bug!("dataflow::sanity_check cannot feed a constant to rustc_peek."); - } - }; + each_block(tcx, mir, flow_ctxt, results, bb); + } +} - let mut entry = results.0.sets.on_entry_set_for(bb.index()).to_owned(); - let mut gen = results.0.sets.gen_set_for(bb.index()).to_owned(); - let mut kill = results.0.sets.kill_set_for(bb.index()).to_owned(); +fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &Mir<'tcx>, + flow_ctxt: &O::Ctxt, + results: &DataflowResults, + bb: repr::BasicBlock) where + O: BitDenotation, Idx=MovePathIndex>, O::Ctxt: HasMoveData<'tcx> +{ + let bb_data = mir.basic_block_data(bb); + let &repr::BasicBlockData { ref statements, + ref terminator, + is_cleanup: _ } = bb_data; + + let (args, span) = match is_rustc_peek(tcx, terminator) { + Some(args_and_span) => args_and_span, + None => return, + }; + assert!(args.len() == 1); + let peek_arg_lval = match args[0] { + repr::Operand::Consume(ref lval @ repr::Lvalue::Temp(_)) => { + lval + } + repr::Operand::Consume(_) | + repr::Operand::Constant(_) => { + tcx.sess.diagnostic().span_err( + span, "dataflow::sanity_check cannot feed a non-temp to rustc_peek."); + return; + } + }; - let move_data = flow_ctxt.move_data(); + let mut entry = results.0.sets.on_entry_set_for(bb.index()).to_owned(); + let mut gen = results.0.sets.gen_set_for(bb.index()).to_owned(); + let mut kill = results.0.sets.kill_set_for(bb.index()).to_owned(); - // Emulate effect of all statements in the block up to (but - // not including) the borrow within `peek_arg_lval`. Do *not* - // include call to `peek_arg_lval` itself (since we are - // peeking the state of the argument at time immediate - // preceding Call to `rustc_peek`). + let move_data = flow_ctxt.move_data(); - let mut sets = super::BlockSets { on_entry: &mut entry, - gen_set: &mut gen, - kill_set: &mut kill }; + // Emulate effect of all statements in the block up to (but not + // including) the borrow within `peek_arg_lval`. Do *not* include + // call to `peek_arg_lval` itself (since we are peeking the state + // of the argument at time immediate preceding Call to + // `rustc_peek`). - for (j, stmt) in statements.iter().enumerate() { - debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt); - let (lvalue, rvalue) = match stmt.kind { - repr::StatementKind::Assign(ref lvalue, ref rvalue) => { - (lvalue, rvalue) - } - }; - - if lvalue == peek_arg_lval { - if let repr::Rvalue::Ref(_, - repr::BorrowKind::Shared, - ref peeking_at_lval) = *rvalue { - // Okay, our search is over. - let peek_mpi = move_data.rev_lookup.find(peeking_at_lval); - let bit_state = sets.on_entry.contains(&peek_mpi); - debug!("rustc_peek({:?} = &{:?}) bit_state: {}", - lvalue, peeking_at_lval, bit_state); - if !bit_state { - tcx.sess.span_err(span, &format!("rustc_peek: bit not set")); - } - continue 'next_block; - } else { - // Our search should have been over, but the input - // does not match expectations of `rustc_peek` for - // this sanity_check. - tcx.sess.span_err(span, &format!("rustc_peek: argument expression \ - must be immediate borrow of form `&expr`")); - } - } + let mut sets = super::BlockSets { on_entry: &mut entry, + gen_set: &mut gen, + kill_set: &mut kill }; - let lhs_mpi = move_data.rev_lookup.find(lvalue); + for (j, stmt) in statements.iter().enumerate() { + debug!("rustc_peek: ({:?},{}) {:?}", bb, j, stmt); + let (lvalue, rvalue) = match stmt.kind { + repr::StatementKind::Assign(ref lvalue, ref rvalue) => { + (lvalue, rvalue) + } + }; - debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}", - lvalue, lhs_mpi, stmt); - // reset GEN and KILL sets before emulating their effect. - for e in sets.gen_set.words_mut() { *e = 0; } - for e in sets.kill_set.words_mut() { *e = 0; } - results.0.operator.statement_effect(flow_ctxt, &mut sets, bb, j); - sets.on_entry.union(sets.gen_set); - sets.on_entry.subtract(sets.kill_set); + if lvalue == peek_arg_lval { + if let repr::Rvalue::Ref(_, + repr::BorrowKind::Shared, + ref peeking_at_lval) = *rvalue { + // Okay, our search is over. + let peek_mpi = move_data.rev_lookup.find(peeking_at_lval); + let bit_state = sets.on_entry.contains(&peek_mpi); + debug!("rustc_peek({:?} = &{:?}) bit_state: {}", + lvalue, peeking_at_lval, bit_state); + if !bit_state { + tcx.sess.span_err(span, &format!("rustc_peek: bit not set")); + } + return; + } else { + // Our search should have been over, but the input + // does not match expectations of `rustc_peek` for + // this sanity_check. + let msg = &format!("rustc_peek: argument expression \ + must be immediate borrow of form `&expr`"); + tcx.sess.span_err(span, msg); + } } - - tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \ - anticipated pattern; note that \ - rustc_peek expects input of \ - form `&expr`")); + + let lhs_mpi = move_data.rev_lookup.find(lvalue); + + debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}", + lvalue, lhs_mpi, stmt); + // reset GEN and KILL sets before emulating their effect. + for e in sets.gen_set.words_mut() { *e = 0; } + for e in sets.kill_set.words_mut() { *e = 0; } + results.0.operator.statement_effect(flow_ctxt, &mut sets, bb, j); + sets.on_entry.union(sets.gen_set); + sets.on_entry.subtract(sets.kill_set); } + tcx.sess.span_err(span, &format!("rustc_peek: MIR did not match \ + anticipated pattern; note that \ + rustc_peek expects input of \ + form `&expr`")); } fn is_rustc_peek<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, From ae09c5e36e3f6da1d20a9764e87ff2a18b3a2985 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 13:26:54 +0200 Subject: [PATCH 34/48] Moved the four impls of `BitDenotation` to their own module, `mod impls`. --- .../borrowck/mir/dataflow/impls.rs | 584 ++++++++++++++++++ .../borrowck/mir/dataflow/mod.rs | 573 +---------------- 2 files changed, 588 insertions(+), 569 deletions(-) create mode 100644 src/librustc_borrowck/borrowck/mir/dataflow/impls.rs diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs new file mode 100644 index 0000000000000..c9a23e3f288b5 --- /dev/null +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -0,0 +1,584 @@ +// Copyright 2012-2016 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. + +use rustc::ty::TyCtxt; +use rustc::mir::repr::{self, Mir}; + +use super::super::gather_moves::{Location}; +use super::super::gather_moves::{MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex}; +use super::super::DropFlagState; +use super::super::drop_flag_effects_for_function_entry; +use super::super::drop_flag_effects_for_location; +use super::super::on_all_children_bits; + +use super::{BitDenotation, BlockSets, DataflowOperator}; + +use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep. +use bitslice::{BitwiseOperator}; +use indexed_set::{Idx, IdxSet}; + +use std::marker::PhantomData; + +// Dataflow analyses are built upon some interpretation of the +// bitvectors attached to each basic block, represented via a +// zero-sized structure. +// +// Note on PhantomData: Each interpretation will need to instantiate +// the `Bit` and `Ctxt` associated types, and in this case, those +// associated types need an associated lifetime `'tcx`. The +// interpretive structures are zero-sized, so they all need to carry a +// `PhantomData` representing how the structures relate to the `'tcx` +// lifetime. +// +// But, since all of the uses of `'tcx` are solely via instances of +// `Ctxt` that are passed into the `BitDenotation` methods, we can +// consistently use a `PhantomData` that is just a function over a +// `&Ctxt` (== `&MoveData<'tcx>). + +/// `MaybeInitializedLvals` tracks all l-values that might be +/// initialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // maybe-init: +/// // {} +/// let a = S; let b = S; let c; let d; // {a, b} +/// +/// if pred { +/// drop(a); // { b} +/// b = S; // { b} +/// +/// } else { +/// drop(b); // {a} +/// d = S; // {a, d} +/// +/// } // {a, b, d} +/// +/// c = S; // {a, b, c, d} +/// } +/// ``` +/// +/// To determine whether an l-value *must* be initialized at a +/// particular control-flow point, one can take the set-difference +/// between this data and the data from `MaybeUninitializedLvals` at the +/// corresponding control-flow point. +/// +/// Similarly, at a given `drop` statement, the set-intersection +/// between this data and `MaybeUninitializedLvals` yields the set of +/// l-values that would require a dynamic drop-flag at that statement. +#[derive(Debug, Default)] +pub struct MaybeInitializedLvals<'a, 'tcx: 'a> { + // See "Note on PhantomData" above. + phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> +} + +/// `MaybeUninitializedLvals` tracks all l-values that might be +/// uninitialized upon reaching a particular point in the control flow +/// for a function. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // maybe-uninit: +/// // {a, b, c, d} +/// let a = S; let b = S; let c; let d; // { c, d} +/// +/// if pred { +/// drop(a); // {a, c, d} +/// b = S; // {a, c, d} +/// +/// } else { +/// drop(b); // { b, c, d} +/// d = S; // { b, c } +/// +/// } // {a, b, c, d} +/// +/// c = S; // {a, b, d} +/// } +/// ``` +/// +/// To determine whether an l-value *must* be uninitialized at a +/// particular control-flow point, one can take the set-difference +/// between this data and the data from `MaybeInitializedLvals` at the +/// corresponding control-flow point. +/// +/// Similarly, at a given `drop` statement, the set-intersection +/// between this data and `MaybeInitializedLvals` yields the set of +/// l-values that would require a dynamic drop-flag at that statement. +#[derive(Debug, Default)] +pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> { + // See "Note on PhantomData" above. + phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> +} + +/// `DefinitelyInitializedLvals` tracks all l-values that are definitely +/// initialized upon reaching a particular point in the control flow +/// for a function. +/// +/// FIXME: Note that once flow-analysis is complete, this should be +/// the set-complement of MaybeUninitializedLvals; thus we can get rid +/// of one or the other of these two. I'm inclined to get rid of +/// MaybeUninitializedLvals, simply because the sets will tend to be +/// smaller in this analysis and thus easier for humans to process +/// when debugging. +/// +/// For example, in code like the following, we have corresponding +/// dataflow information shown in the right-hand comments. +/// +/// ```rust +/// struct S; +/// fn foo(pred: bool) { // definite-init: +/// // { } +/// let a = S; let b = S; let c; let d; // {a, b } +/// +/// if pred { +/// drop(a); // { b, } +/// b = S; // { b, } +/// +/// } else { +/// drop(b); // {a, } +/// d = S; // {a, d} +/// +/// } // { } +/// +/// c = S; // { c } +/// } +/// ``` +/// +/// To determine whether an l-value *may* be uninitialized at a +/// particular control-flow point, one can take the set-complement +/// of this data. +/// +/// Similarly, at a given `drop` statement, the set-difference between +/// this data and `MaybeInitializedLvals` yields the set of l-values +/// that would require a dynamic drop-flag at that statement. +#[derive(Debug, Default)] +pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> { + // See "Note on PhantomData" above. + phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> +} + +/// `MovingOutStatements` tracks the statements that perform moves out +/// of particular l-values. More precisely, it tracks whether the +/// *effect* of such moves (namely, the uninitialization of the +/// l-value in question) can reach some point in the control-flow of +/// the function, or if that effect is "killed" by some intervening +/// operation reinitializing that l-value. +/// +/// The resulting dataflow is a more enriched version of +/// `MaybeUninitializedLvals`. Both structures on their own only tell +/// you if an l-value *might* be uninitialized at a given point in the +/// control flow. But `MovingOutStatements` also includes the added +/// data of *which* particular statement causing the deinitialization +/// that the borrow checker's error meessage may need to report. +#[derive(Debug, Default)] +pub struct MovingOutStatements<'a, 'tcx: 'a> { + // See "Note on PhantomData" above. + phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> +} + +impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + state: DropFlagState) + { + match state { + DropFlagState::Absent => sets.kill(&path), + DropFlagState::Present => sets.gen(&path), + } + } +} + +impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + state: DropFlagState) + { + match state { + DropFlagState::Absent => sets.gen(&path), + DropFlagState::Present => sets.kill(&path), + } + } +} + +impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { + fn update_bits(sets: &mut BlockSets, path: MovePathIndex, + state: DropFlagState) + { + match state { + DropFlagState::Absent => sets.kill(&path), + DropFlagState::Present => sets.gen(&path), + } + } +} + +impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { + type Idx = MovePathIndex; + type Bit = MovePath<'tcx>; + type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + fn name() -> &'static str { "maybe_init" } + fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { + ctxt.2.move_paths.len() + } + fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { + &ctxt.2.move_paths[MovePathIndex::new(idx)] + } + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) + { + drop_flag_effects_for_function_entry( + ctxt.0, ctxt.1, &ctxt.2, + |path, s| { + assert!(s == DropFlagState::Present); + sets.on_entry.add(&path); + }); + } + + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx: usize) + { + drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: idx }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + statements_len: usize) + { + drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: statements_len }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut IdxSet, + _call_bb: repr::BasicBlock, + _dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue) { + // when a call returns successfully, that means we need to set + // the bits for that dest_lval to 1 (initialized). + let move_data = &ctxt.2; + let move_path_index = move_data.rev_lookup.find(dest_lval); + on_all_children_bits(ctxt.0, ctxt.1, &ctxt.2, + move_path_index, + |mpi| { in_out.add(&mpi); }); + } +} + +impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { + type Idx = MovePathIndex; + type Bit = MovePath<'tcx>; + type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + fn name() -> &'static str { "maybe_uninit" } + fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { + ctxt.2.move_paths.len() + } + fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { + &ctxt.2.move_paths[MovePathIndex::new(idx)] + } + + // sets on_entry bits for Arg lvalues + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { + // set all bits to 1 (uninit) before gathering counterevidence + for e in sets.on_entry.words_mut() { *e = !0; } + + drop_flag_effects_for_function_entry( + ctxt.0, ctxt.1, &ctxt.2, + |path, s| { + assert!(s == DropFlagState::Present); + sets.on_entry.remove(&path); + }); + } + + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx: usize) + { + drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: idx }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + statements_len: usize) + { + drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: statements_len }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut IdxSet, + _call_bb: repr::BasicBlock, + _dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue) { + // when a call returns successfully, that means we need to set + // the bits for that dest_lval to 1 (initialized). + let move_path_index = ctxt.2.rev_lookup.find(dest_lval); + on_all_children_bits(ctxt.0, ctxt.1, &ctxt.2, + move_path_index, + |mpi| { in_out.remove(&mpi); }); + } +} + +impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { + type Idx = MovePathIndex; + type Bit = MovePath<'tcx>; + type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + fn name() -> &'static str { "definite_init" } + fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { + ctxt.2.move_paths.len() + } + fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { + &ctxt.2.move_paths[MovePathIndex::new(idx)] + } + + // sets on_entry bits for Arg lvalues + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { + for e in sets.on_entry.words_mut() { *e = 0; } + + drop_flag_effects_for_function_entry( + ctxt.0, ctxt.1, &ctxt.2, + |path, s| { + assert!(s == DropFlagState::Present); + sets.on_entry.add(&path); + }); + } + + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx: usize) + { + drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: idx }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + statements_len: usize) + { + drop_flag_effects_for_location( + ctxt.0, ctxt.1, &ctxt.2, + Location { block: bb, index: statements_len }, + |path, s| Self::update_bits(sets, path, s) + ) + } + + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut IdxSet, + _call_bb: repr::BasicBlock, + _dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue) { + // when a call returns successfully, that means we need to set + // the bits for that dest_lval to 1 (initialized). + let move_path_index = ctxt.2.rev_lookup.find(dest_lval); + on_all_children_bits(ctxt.0, ctxt.1, &ctxt.2, + move_path_index, + |mpi| { in_out.add(&mpi); }); + } +} + +impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { + type Idx = MoveOutIndex; + type Bit = MoveOut; + type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + fn name() -> &'static str { "moving_out" } + fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { + ctxt.2.moves.len() + } + fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { + &ctxt.2.moves[idx] + } + fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) { + // no move-statements have been executed prior to function + // execution, so this method has no effect on `_sets`. + } + fn statement_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + idx: usize) { + let &(tcx, mir, ref move_data) = ctxt; + let stmt = &mir.basic_block_data(bb).statements[idx]; + let loc_map = &move_data.loc_map; + let path_map = &move_data.path_map; + let rev_lookup = &move_data.rev_lookup; + + let loc = Location { block: bb, index: idx }; + debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}", + stmt, loc, &loc_map[loc]); + for move_index in &loc_map[loc] { + // Every path deinitialized by a *particular move* + // has corresponding bit, "gen'ed" (i.e. set) + // here, in dataflow vector + zero_to_one(sets.gen_set.words_mut(), *move_index); + } + let bits_per_block = self.bits_per_block(ctxt); + match stmt.kind { + repr::StatementKind::Assign(ref lvalue, _) => { + // assigning into this `lvalue` kills all + // MoveOuts from it, and *also* all MoveOuts + // for children and associated fragment sets. + let move_path_index = rev_lookup.find(lvalue); + on_all_children_bits(tcx, + mir, + move_data, + move_path_index, + |mpi| for moi in &path_map[mpi] { + assert!(moi.idx() < bits_per_block); + sets.kill_set.add(&moi); + }); + } + } + } + + fn terminator_effect(&self, + ctxt: &Self::Ctxt, + sets: &mut BlockSets, + bb: repr::BasicBlock, + statements_len: usize) + { + let &(_tcx, mir, ref move_data) = ctxt; + let term = mir.basic_block_data(bb).terminator.as_ref().unwrap(); + let loc_map = &move_data.loc_map; + let loc = Location { block: bb, index: statements_len }; + debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}", + term, loc, &loc_map[loc]); + let bits_per_block = self.bits_per_block(ctxt); + for move_index in &loc_map[loc] { + assert!(move_index.idx() < bits_per_block); + zero_to_one(sets.gen_set.words_mut(), *move_index); + } + } + + fn propagate_call_return(&self, + ctxt: &Self::Ctxt, + in_out: &mut IdxSet, + _call_bb: repr::BasicBlock, + _dest_bb: repr::BasicBlock, + dest_lval: &repr::Lvalue) { + let move_data = &ctxt.2; + let move_path_index = move_data.rev_lookup.find(dest_lval); + let bits_per_block = self.bits_per_block(ctxt); + + let path_map = &move_data.path_map; + on_all_children_bits(ctxt.0, + ctxt.1, + move_data, + move_path_index, + |mpi| for moi in &path_map[mpi] { + assert!(moi.idx() < bits_per_block); + in_out.remove(&moi); + }); + } +} + +fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) { + let retval = bitvec.set_bit(move_index.idx()); + assert!(retval); +} + +impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // moves from both preds are in scope + } +} + +impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // "maybe" means we union effects of both preds + } +} + +impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 | pred2 // "maybe" means we union effects of both preds + } +} + +impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { + #[inline] + fn join(&self, pred1: usize, pred2: usize) -> usize { + pred1 & pred2 // "definitely" means we intersect effects of both preds + } +} + +// The way that dataflow fixed point iteration works, you want to +// start at bottom and work your way to a fixed point. Control-flow +// merges will apply the `join` operator to each block entry's current +// state (which starts at that bottom value). +// +// This means, for propagation across the graph, that you either want +// to start at all-zeroes and then use Union as your merge when +// propagating, or you start at all-ones and then use Intersect as +// your merge when propagating. + +impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> { + #[inline] + fn bottom_value() -> bool { + false // bottom = no loans in scope by default + } +} + +impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> { + #[inline] + fn bottom_value() -> bool { + false // bottom = uninitialized + } +} + +impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> { + #[inline] + fn bottom_value() -> bool { + false // bottom = initialized (start_block_effect counters this at outset) + } +} + +impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> { + #[inline] + fn bottom_value() -> bool { + true // bottom = initialized (start_block_effect counters this at outset) + } +} diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index e1459ad2abeae..97e29fe064feb 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -13,24 +13,23 @@ use rustc::mir::repr::{self, Mir}; use std::fmt::Debug; use std::io; -use std::marker::PhantomData; use std::mem; use std::path::PathBuf; use std::usize; use super::MirBorrowckCtxtPreDataflow; -use super::gather_moves::{Location, MoveData, MovePathIndex, MoveOutIndex}; -use super::gather_moves::{MoveOut, MovePath}; -use super::DropFlagState; +use super::gather_moves::{MoveData}; -use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep. use bitslice::{bitwise, BitwiseOperator}; use indexed_set::{Idx, IdxSet, OwnIdxSet}; pub use self::sanity_check::sanity_check_via_rustc_peek; +pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals}; +pub use self::impls::{DefinitelyInitializedLvals, MovingOutStatements}; mod graphviz; mod sanity_check; +mod impls; pub trait Dataflow { fn dataflow(&mut self); @@ -550,567 +549,3 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> } } } - -// Dataflow analyses are built upon some interpretation of the -// bitvectors attached to each basic block, represented via a -// zero-sized structure. -// -// Note on PhantomData: Each interpretation will need to instantiate -// the `Bit` and `Ctxt` associated types, and in this case, those -// associated types need an associated lifetime `'tcx`. The -// interpretive structures are zero-sized, so they all need to carry a -// `PhantomData` representing how the structures relate to the `'tcx` -// lifetime. -// -// But, since all of the uses of `'tcx` are solely via instances of -// `Ctxt` that are passed into the `BitDenotation` methods, we can -// consistently use a `PhantomData` that is just a function over a -// `&Ctxt` (== `&MoveData<'tcx>). - -/// `MaybeInitializedLvals` tracks all l-values that might be -/// initialized upon reaching a particular point in the control flow -/// for a function. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // maybe-init: -/// // {} -/// let a = S; let b = S; let c; let d; // {a, b} -/// -/// if pred { -/// drop(a); // { b} -/// b = S; // { b} -/// -/// } else { -/// drop(b); // {a} -/// d = S; // {a, d} -/// -/// } // {a, b, d} -/// -/// c = S; // {a, b, c, d} -/// } -/// ``` -/// -/// To determine whether an l-value *must* be initialized at a -/// particular control-flow point, one can take the set-difference -/// between this data and the data from `MaybeUninitializedLvals` at the -/// corresponding control-flow point. -/// -/// Similarly, at a given `drop` statement, the set-intersection -/// between this data and `MaybeUninitializedLvals` yields the set of -/// l-values that would require a dynamic drop-flag at that statement. -#[derive(Debug, Default)] -pub struct MaybeInitializedLvals<'a, 'tcx: 'a> { - // See "Note on PhantomData" above. - phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> -} - -/// `MaybeUninitializedLvals` tracks all l-values that might be -/// uninitialized upon reaching a particular point in the control flow -/// for a function. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // maybe-uninit: -/// // {a, b, c, d} -/// let a = S; let b = S; let c; let d; // { c, d} -/// -/// if pred { -/// drop(a); // {a, c, d} -/// b = S; // {a, c, d} -/// -/// } else { -/// drop(b); // { b, c, d} -/// d = S; // { b, c } -/// -/// } // {a, b, c, d} -/// -/// c = S; // {a, b, d} -/// } -/// ``` -/// -/// To determine whether an l-value *must* be uninitialized at a -/// particular control-flow point, one can take the set-difference -/// between this data and the data from `MaybeInitializedLvals` at the -/// corresponding control-flow point. -/// -/// Similarly, at a given `drop` statement, the set-intersection -/// between this data and `MaybeInitializedLvals` yields the set of -/// l-values that would require a dynamic drop-flag at that statement. -#[derive(Debug, Default)] -pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> { - // See "Note on PhantomData" above. - phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> -} - -/// `DefinitelyInitializedLvals` tracks all l-values that are definitely -/// initialized upon reaching a particular point in the control flow -/// for a function. -/// -/// FIXME: Note that once flow-analysis is complete, this should be -/// the set-complement of MaybeUninitializedLvals; thus we can get rid -/// of one or the other of these two. I'm inclined to get rid of -/// MaybeUninitializedLvals, simply because the sets will tend to be -/// smaller in this analysis and thus easier for humans to process -/// when debugging. -/// -/// For example, in code like the following, we have corresponding -/// dataflow information shown in the right-hand comments. -/// -/// ```rust -/// struct S; -/// fn foo(pred: bool) { // definite-init: -/// // { } -/// let a = S; let b = S; let c; let d; // {a, b } -/// -/// if pred { -/// drop(a); // { b, } -/// b = S; // { b, } -/// -/// } else { -/// drop(b); // {a, } -/// d = S; // {a, d} -/// -/// } // { } -/// -/// c = S; // { c } -/// } -/// ``` -/// -/// To determine whether an l-value *may* be uninitialized at a -/// particular control-flow point, one can take the set-complement -/// of this data. -/// -/// Similarly, at a given `drop` statement, the set-difference between -/// this data and `MaybeInitializedLvals` yields the set of l-values -/// that would require a dynamic drop-flag at that statement. -#[derive(Debug, Default)] -pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> { - // See "Note on PhantomData" above. - phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> -} - -/// `MovingOutStatements` tracks the statements that perform moves out -/// of particular l-values. More precisely, it tracks whether the -/// *effect* of such moves (namely, the uninitialization of the -/// l-value in question) can reach some point in the control-flow of -/// the function, or if that effect is "killed" by some intervening -/// operation reinitializing that l-value. -/// -/// The resulting dataflow is a more enriched version of -/// `MaybeUninitializedLvals`. Both structures on their own only tell -/// you if an l-value *might* be uninitialized at a given point in the -/// control flow. But `MovingOutStatements` also includes the added -/// data of *which* particular statement causing the deinitialization -/// that the borrow checker's error meessage may need to report. -#[derive(Debug, Default)] -pub struct MovingOutStatements<'a, 'tcx: 'a> { - // See "Note on PhantomData" above. - phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> -} - -impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { - type Idx = MoveOutIndex; - type Bit = MoveOut; - type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); - fn name() -> &'static str { "moving_out" } - fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.2.moves.len() - } - fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.2.moves[idx] - } - fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) { - // no move-statements have been executed prior to function - // execution, so this method has no effect on `_sets`. - } - fn statement_effect(&self, - ctxt: &Self::Ctxt, - sets: &mut BlockSets, - bb: repr::BasicBlock, - idx: usize) { - let &(tcx, mir, ref move_data) = ctxt; - let stmt = &mir.basic_block_data(bb).statements[idx]; - let loc_map = &move_data.loc_map; - let path_map = &move_data.path_map; - let rev_lookup = &move_data.rev_lookup; - - let loc = Location { block: bb, index: idx }; - debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}", - stmt, loc, &loc_map[loc]); - for move_index in &loc_map[loc] { - // Every path deinitialized by a *particular move* - // has corresponding bit, "gen'ed" (i.e. set) - // here, in dataflow vector - zero_to_one(sets.gen_set.words_mut(), *move_index); - } - let bits_per_block = self.bits_per_block(ctxt); - match stmt.kind { - repr::StatementKind::Assign(ref lvalue, _) => { - // assigning into this `lvalue` kills all - // MoveOuts from it, and *also* all MoveOuts - // for children and associated fragment sets. - let move_path_index = rev_lookup.find(lvalue); - super::on_all_children_bits(tcx, - mir, - move_data, - move_path_index, - |mpi| for moi in &path_map[mpi] { - assert!(moi.idx() < bits_per_block); - sets.kill_set.add(&moi); - }); - } - } - } - - fn terminator_effect(&self, - ctxt: &Self::Ctxt, - sets: &mut BlockSets, - bb: repr::BasicBlock, - statements_len: usize) - { - let &(_tcx, mir, ref move_data) = ctxt; - let term = mir.basic_block_data(bb).terminator.as_ref().unwrap(); - let loc_map = &move_data.loc_map; - let loc = Location { block: bb, index: statements_len }; - debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}", - term, loc, &loc_map[loc]); - let bits_per_block = self.bits_per_block(ctxt); - for move_index in &loc_map[loc] { - assert!(move_index.idx() < bits_per_block); - zero_to_one(sets.gen_set.words_mut(), *move_index); - } - } - - fn propagate_call_return(&self, - ctxt: &Self::Ctxt, - in_out: &mut IdxSet, - _call_bb: repr::BasicBlock, - _dest_bb: repr::BasicBlock, - dest_lval: &repr::Lvalue) { - let move_data = &ctxt.2; - let move_path_index = move_data.rev_lookup.find(dest_lval); - let bits_per_block = self.bits_per_block(ctxt); - - let path_map = &move_data.path_map; - super::on_all_children_bits(ctxt.0, - ctxt.1, - move_data, - move_path_index, - |mpi| for moi in &path_map[mpi] { - assert!(moi.idx() < bits_per_block); - in_out.remove(&moi); - }); - } -} - -impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { - fn update_bits(sets: &mut BlockSets, path: MovePathIndex, - state: super::DropFlagState) - { - match state { - DropFlagState::Absent => sets.kill(&path), - DropFlagState::Present => sets.gen(&path), - } - } -} - -impl<'a, 'tcx> MaybeUninitializedLvals<'a, 'tcx> { - fn update_bits(sets: &mut BlockSets, path: MovePathIndex, - state: super::DropFlagState) - { - match state { - DropFlagState::Absent => sets.gen(&path), - DropFlagState::Present => sets.kill(&path), - } - } -} - -impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { - fn update_bits(sets: &mut BlockSets, path: MovePathIndex, - state: super::DropFlagState) - { - match state { - DropFlagState::Absent => sets.kill(&path), - DropFlagState::Present => sets.gen(&path), - } - } -} - -impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { - type Idx = MovePathIndex; - type Bit = MovePath<'tcx>; - type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); - fn name() -> &'static str { "maybe_init" } - fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.2.move_paths.len() - } - fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.2.move_paths[MovePathIndex::new(idx)] - } - fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) - { - super::drop_flag_effects_for_function_entry( - ctxt.0, ctxt.1, &ctxt.2, - |path, s| { - assert!(s == DropFlagState::Present); - sets.on_entry.add(&path); - }); - } - - fn statement_effect(&self, - ctxt: &Self::Ctxt, - sets: &mut BlockSets, - bb: repr::BasicBlock, - idx: usize) - { - super::drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, - Location { block: bb, index: idx }, - |path, s| Self::update_bits(sets, path, s) - ) - } - - fn terminator_effect(&self, - ctxt: &Self::Ctxt, - sets: &mut BlockSets, - bb: repr::BasicBlock, - statements_len: usize) - { - super::drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, - Location { block: bb, index: statements_len }, - |path, s| Self::update_bits(sets, path, s) - ) - } - - fn propagate_call_return(&self, - ctxt: &Self::Ctxt, - in_out: &mut IdxSet, - _call_bb: repr::BasicBlock, - _dest_bb: repr::BasicBlock, - dest_lval: &repr::Lvalue) { - // when a call returns successfully, that means we need to set - // the bits for that dest_lval to 1 (initialized). - let move_data = &ctxt.2; - let move_path_index = move_data.rev_lookup.find(dest_lval); - super::on_all_children_bits( - ctxt.0, ctxt.1, &ctxt.2, - move_path_index, - |mpi| { in_out.add(&mpi); } - ); - } -} - -impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { - type Idx = MovePathIndex; - type Bit = MovePath<'tcx>; - type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); - fn name() -> &'static str { "maybe_uninit" } - fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.2.move_paths.len() - } - fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.2.move_paths[MovePathIndex::new(idx)] - } - - // sets on_entry bits for Arg lvalues - fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { - // set all bits to 1 (uninit) before gathering counterevidence - for e in sets.on_entry.words_mut() { *e = !0; } - - super::drop_flag_effects_for_function_entry( - ctxt.0, ctxt.1, &ctxt.2, - |path, s| { - assert!(s == DropFlagState::Present); - sets.on_entry.remove(&path); - }); - } - - fn statement_effect(&self, - ctxt: &Self::Ctxt, - sets: &mut BlockSets, - bb: repr::BasicBlock, - idx: usize) - { - super::drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, - Location { block: bb, index: idx }, - |path, s| Self::update_bits(sets, path, s) - ) - } - - fn terminator_effect(&self, - ctxt: &Self::Ctxt, - sets: &mut BlockSets, - bb: repr::BasicBlock, - statements_len: usize) - { - super::drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, - Location { block: bb, index: statements_len }, - |path, s| Self::update_bits(sets, path, s) - ) - } - - fn propagate_call_return(&self, - ctxt: &Self::Ctxt, - in_out: &mut IdxSet, - _call_bb: repr::BasicBlock, - _dest_bb: repr::BasicBlock, - dest_lval: &repr::Lvalue) { - // when a call returns successfully, that means we need to set - // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.2.rev_lookup.find(dest_lval); - super::on_all_children_bits( - ctxt.0, ctxt.1, &ctxt.2, - move_path_index, - |mpi| { in_out.remove(&mpi); } - ); - } -} - -impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { - type Idx = MovePathIndex; - type Bit = MovePath<'tcx>; - type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); - fn name() -> &'static str { "definite_init" } - fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.2.move_paths.len() - } - fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.2.move_paths[MovePathIndex::new(idx)] - } - - // sets on_entry bits for Arg lvalues - fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { - for e in sets.on_entry.words_mut() { *e = 0; } - - super::drop_flag_effects_for_function_entry( - ctxt.0, ctxt.1, &ctxt.2, - |path, s| { - assert!(s == DropFlagState::Present); - sets.on_entry.add(&path); - }); - } - - fn statement_effect(&self, - ctxt: &Self::Ctxt, - sets: &mut BlockSets, - bb: repr::BasicBlock, - idx: usize) - { - super::drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, - Location { block: bb, index: idx }, - |path, s| Self::update_bits(sets, path, s) - ) - } - - fn terminator_effect(&self, - ctxt: &Self::Ctxt, - sets: &mut BlockSets, - bb: repr::BasicBlock, - statements_len: usize) - { - super::drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, - Location { block: bb, index: statements_len }, - |path, s| Self::update_bits(sets, path, s) - ) - } - - fn propagate_call_return(&self, - ctxt: &Self::Ctxt, - in_out: &mut IdxSet, - _call_bb: repr::BasicBlock, - _dest_bb: repr::BasicBlock, - dest_lval: &repr::Lvalue) { - // when a call returns successfully, that means we need to set - // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.2.rev_lookup.find(dest_lval); - super::on_all_children_bits( - ctxt.0, ctxt.1, &ctxt.2, - move_path_index, - |mpi| { in_out.add(&mpi); } - ); - } -} - -fn zero_to_one(bitvec: &mut [usize], move_index: MoveOutIndex) { - let retval = bitvec.set_bit(move_index.idx()); - assert!(retval); -} - -impl<'a, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'tcx> { - #[inline] - fn join(&self, pred1: usize, pred2: usize) -> usize { - pred1 | pred2 // moves from both preds are in scope - } -} - -impl<'a, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'tcx> { - #[inline] - fn join(&self, pred1: usize, pred2: usize) -> usize { - pred1 | pred2 // "maybe" means we union effects of both preds - } -} - -impl<'a, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'tcx> { - #[inline] - fn join(&self, pred1: usize, pred2: usize) -> usize { - pred1 | pred2 // "maybe" means we union effects of both preds - } -} - -impl<'a, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'tcx> { - #[inline] - fn join(&self, pred1: usize, pred2: usize) -> usize { - pred1 & pred2 // "definitely" means we intersect effects of both preds - } -} - -// The way that dataflow fixed point iteration works, you want to -// start at bottom and work your way to a fixed point. Control-flow -// merges will apply the `join` operator to each block entry's current -// state (which starts at that bottom value). -// -// This means, for propagation across the graph, that you either want -// to start at all-zeroes and then use Union as your merge when -// propagating, or you start at all-ones and then use Intersect as -// your merge when propagating. - -impl<'a, 'tcx> DataflowOperator for MovingOutStatements<'a, 'tcx> { - #[inline] - fn bottom_value() -> bool { - false // bottom = no loans in scope by default - } -} - -impl<'a, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'tcx> { - #[inline] - fn bottom_value() -> bool { - false // bottom = uninitialized - } -} - -impl<'a, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'tcx> { - #[inline] - fn bottom_value() -> bool { - false // bottom = initialized (start_block_effect counters this at outset) - } -} - -impl<'a, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'tcx> { - #[inline] - fn bottom_value() -> bool { - true // bottom = initialized - } -} - From 221cce915a4dfae69ca42536215a5b0c0538047e Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 15:01:48 +0200 Subject: [PATCH 35/48] move the `tcx` and `mir` parts of associated `Ctxt` onto each `BitDenotation` impl. --- .../borrowck/mir/dataflow/impls.rs | 118 +++++++++--------- .../borrowck/mir/dataflow/mod.rs | 12 +- src/librustc_borrowck/borrowck/mir/mod.rs | 26 ++-- 3 files changed, 77 insertions(+), 79 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs index c9a23e3f288b5..480b890da6ad3 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -24,23 +24,9 @@ use bitslice::BitSlice; // adds set_bit/get_bit to &[usize] bitvector rep. use bitslice::{BitwiseOperator}; use indexed_set::{Idx, IdxSet}; -use std::marker::PhantomData; - // Dataflow analyses are built upon some interpretation of the // bitvectors attached to each basic block, represented via a // zero-sized structure. -// -// Note on PhantomData: Each interpretation will need to instantiate -// the `Bit` and `Ctxt` associated types, and in this case, those -// associated types need an associated lifetime `'tcx`. The -// interpretive structures are zero-sized, so they all need to carry a -// `PhantomData` representing how the structures relate to the `'tcx` -// lifetime. -// -// But, since all of the uses of `'tcx` are solely via instances of -// `Ctxt` that are passed into the `BitDenotation` methods, we can -// consistently use a `PhantomData` that is just a function over a -// `&Ctxt` (== `&MoveData<'tcx>). /// `MaybeInitializedLvals` tracks all l-values that might be /// initialized upon reaching a particular point in the control flow @@ -77,10 +63,15 @@ use std::marker::PhantomData; /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeUninitializedLvals` yields the set of /// l-values that would require a dynamic drop-flag at that statement. -#[derive(Debug, Default)] pub struct MaybeInitializedLvals<'a, 'tcx: 'a> { - // See "Note on PhantomData" above. - phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, +} + +impl<'a, 'tcx: 'a> MaybeInitializedLvals<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + MaybeInitializedLvals { tcx: tcx, mir: mir } + } } /// `MaybeUninitializedLvals` tracks all l-values that might be @@ -118,10 +109,15 @@ pub struct MaybeInitializedLvals<'a, 'tcx: 'a> { /// Similarly, at a given `drop` statement, the set-intersection /// between this data and `MaybeInitializedLvals` yields the set of /// l-values that would require a dynamic drop-flag at that statement. -#[derive(Debug, Default)] pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> { - // See "Note on PhantomData" above. - phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, +} + +impl<'a, 'tcx: 'a> MaybeUninitializedLvals<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + MaybeUninitializedLvals { tcx: tcx, mir: mir } + } } /// `DefinitelyInitializedLvals` tracks all l-values that are definitely @@ -165,10 +161,15 @@ pub struct MaybeUninitializedLvals<'a, 'tcx: 'a> { /// Similarly, at a given `drop` statement, the set-difference between /// this data and `MaybeInitializedLvals` yields the set of l-values /// that would require a dynamic drop-flag at that statement. -#[derive(Debug, Default)] pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> { - // See "Note on PhantomData" above. - phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, +} + +impl<'a, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>) -> Self { + DefinitelyInitializedLvals { tcx: tcx, mir: mir } + } } /// `MovingOutStatements` tracks the statements that perform moves out @@ -184,10 +185,10 @@ pub struct DefinitelyInitializedLvals<'a, 'tcx: 'a> { /// control flow. But `MovingOutStatements` also includes the added /// data of *which* particular statement causing the deinitialization /// that the borrow checker's error meessage may need to report. -#[derive(Debug, Default)] +#[allow(dead_code)] pub struct MovingOutStatements<'a, 'tcx: 'a> { - // See "Note on PhantomData" above. - phantom: PhantomData, TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>)> + tcx: TyCtxt<'a, 'tcx, 'tcx>, + mir: &'a Mir<'tcx>, } impl<'a, 'tcx> MaybeInitializedLvals<'a, 'tcx> { @@ -226,18 +227,18 @@ impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; type Bit = MovePath<'tcx>; - type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + type Ctxt = MoveData<'tcx>; fn name() -> &'static str { "maybe_init" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.2.move_paths.len() + ctxt.move_paths.len() } fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.2.move_paths[MovePathIndex::new(idx)] + &ctxt.move_paths[MovePathIndex::new(idx)] } fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { drop_flag_effects_for_function_entry( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, |path, s| { assert!(s == DropFlagState::Present); sets.on_entry.add(&path); @@ -251,7 +252,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { idx: usize) { drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, Location { block: bb, index: idx }, |path, s| Self::update_bits(sets, path, s) ) @@ -264,7 +265,7 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { statements_len: usize) { drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, Location { block: bb, index: statements_len }, |path, s| Self::update_bits(sets, path, s) ) @@ -278,9 +279,8 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set // the bits for that dest_lval to 1 (initialized). - let move_data = &ctxt.2; - let move_path_index = move_data.rev_lookup.find(dest_lval); - on_all_children_bits(ctxt.0, ctxt.1, &ctxt.2, + let move_path_index = ctxt.rev_lookup.find(dest_lval); + on_all_children_bits(self.tcx, self.mir, ctxt, move_path_index, |mpi| { in_out.add(&mpi); }); } @@ -289,13 +289,13 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; type Bit = MovePath<'tcx>; - type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + type Ctxt = MoveData<'tcx>; fn name() -> &'static str { "maybe_uninit" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.2.move_paths.len() + ctxt.move_paths.len() } fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.2.move_paths[MovePathIndex::new(idx)] + &ctxt.move_paths[MovePathIndex::new(idx)] } // sets on_entry bits for Arg lvalues @@ -304,7 +304,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { for e in sets.on_entry.words_mut() { *e = !0; } drop_flag_effects_for_function_entry( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, |path, s| { assert!(s == DropFlagState::Present); sets.on_entry.remove(&path); @@ -318,7 +318,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { idx: usize) { drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, Location { block: bb, index: idx }, |path, s| Self::update_bits(sets, path, s) ) @@ -331,7 +331,7 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { statements_len: usize) { drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, Location { block: bb, index: statements_len }, |path, s| Self::update_bits(sets, path, s) ) @@ -345,8 +345,8 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.2.rev_lookup.find(dest_lval); - on_all_children_bits(ctxt.0, ctxt.1, &ctxt.2, + let move_path_index = ctxt.rev_lookup.find(dest_lval); + on_all_children_bits(self.tcx, self.mir, ctxt, move_path_index, |mpi| { in_out.remove(&mpi); }); } @@ -355,13 +355,13 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; type Bit = MovePath<'tcx>; - type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + type Ctxt = MoveData<'tcx>; fn name() -> &'static str { "definite_init" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.2.move_paths.len() + ctxt.move_paths.len() } fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.2.move_paths[MovePathIndex::new(idx)] + &ctxt.move_paths[MovePathIndex::new(idx)] } // sets on_entry bits for Arg lvalues @@ -369,7 +369,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { for e in sets.on_entry.words_mut() { *e = 0; } drop_flag_effects_for_function_entry( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, |path, s| { assert!(s == DropFlagState::Present); sets.on_entry.add(&path); @@ -383,7 +383,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { idx: usize) { drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, Location { block: bb, index: idx }, |path, s| Self::update_bits(sets, path, s) ) @@ -396,7 +396,7 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { statements_len: usize) { drop_flag_effects_for_location( - ctxt.0, ctxt.1, &ctxt.2, + self.tcx, self.mir, ctxt, Location { block: bb, index: statements_len }, |path, s| Self::update_bits(sets, path, s) ) @@ -410,8 +410,8 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.2.rev_lookup.find(dest_lval); - on_all_children_bits(ctxt.0, ctxt.1, &ctxt.2, + let move_path_index = ctxt.rev_lookup.find(dest_lval); + on_all_children_bits(self.tcx, self.mir, ctxt, move_path_index, |mpi| { in_out.add(&mpi); }); } @@ -420,13 +420,13 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { type Idx = MoveOutIndex; type Bit = MoveOut; - type Ctxt = (TyCtxt<'a, 'tcx, 'tcx>, &'a Mir<'tcx>, MoveData<'tcx>); + type Ctxt = MoveData<'tcx>; fn name() -> &'static str { "moving_out" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.2.moves.len() + ctxt.moves.len() } fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.2.moves[idx] + &ctxt.moves[idx] } fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) { // no move-statements have been executed prior to function @@ -437,7 +437,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { sets: &mut BlockSets, bb: repr::BasicBlock, idx: usize) { - let &(tcx, mir, ref move_data) = ctxt; + let (tcx, mir, move_data) = (self.tcx, self.mir, ctxt); let stmt = &mir.basic_block_data(bb).statements[idx]; let loc_map = &move_data.loc_map; let path_map = &move_data.path_map; @@ -477,7 +477,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { bb: repr::BasicBlock, statements_len: usize) { - let &(_tcx, mir, ref move_data) = ctxt; + let (mir, move_data) = (self.mir, ctxt); let term = mir.basic_block_data(bb).terminator.as_ref().unwrap(); let loc_map = &move_data.loc_map; let loc = Location { block: bb, index: statements_len }; @@ -496,13 +496,13 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { _call_bb: repr::BasicBlock, _dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue) { - let move_data = &ctxt.2; + let move_data = ctxt; let move_path_index = move_data.rev_lookup.find(dest_lval); let bits_per_block = self.bits_per_block(ctxt); let path_map = &move_data.path_map; - on_all_children_bits(ctxt.0, - ctxt.1, + on_all_children_bits(self.tcx, + self.mir, move_data, move_path_index, |mpi| for moi in &path_map[mpi] { diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index 97e29fe064feb..e1470e62840da 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -49,7 +49,7 @@ impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> } struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> - where O: 'b + BitDenotation, O::Ctxt: HasMoveData<'tcx> + where O: 'b + BitDenotation, O::Ctxt: 'a+HasMoveData<'tcx> { builder: &'b mut DataflowAnalysis<'a, 'tcx, O>, changed: bool, @@ -191,18 +191,18 @@ impl<'tcx, A, B> HasMoveData<'tcx> for (A, B, MoveData<'tcx>) { } pub struct DataflowAnalysis<'a, 'tcx: 'a, O> - where O: BitDenotation, O::Ctxt: HasMoveData<'tcx> + where O: BitDenotation, O::Ctxt: 'a+HasMoveData<'tcx> { flow_state: DataflowState, - ctxt: O::Ctxt, mir: &'a Mir<'tcx>, + ctxt: &'a O::Ctxt, } impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> where O: BitDenotation, O::Ctxt: HasMoveData<'tcx> { - pub fn results(self) -> (O::Ctxt, DataflowResults) { - (self.ctxt, DataflowResults(self.flow_state)) + pub fn results(self) -> DataflowResults { + DataflowResults(self.flow_state) } pub fn mir(&self) -> &'a Mir<'tcx> { self.mir } @@ -440,7 +440,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> { pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, - ctxt: D::Ctxt, + ctxt: &'a D::Ctxt, denotation: D) -> Self { let bits_per_block = denotation.bits_per_block(&ctxt); let usize_bits = mem::size_of::() * 8; diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 37c042844e58b..54132cf0258da 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -75,24 +75,22 @@ pub fn borrowck_mir<'a, 'tcx: 'a>( let tcx = bcx.tcx; let move_data = MoveData::gather_moves(mir, tcx); - let ctxt = (tcx, mir, move_data); - let (ctxt, flow_inits) = - do_dataflow(tcx, mir, id, attributes, ctxt, MaybeInitializedLvals::default()); - let (ctxt, flow_uninits) = - do_dataflow(tcx, mir, id, attributes, ctxt, MaybeUninitializedLvals::default()); - let (ctxt, flow_def_inits) = - do_dataflow(tcx, mir, id, attributes, ctxt, DefinitelyInitializedLvals::default()); + let flow_inits = + do_dataflow(tcx, mir, id, attributes, &move_data, MaybeInitializedLvals::new(tcx, mir)); + let flow_uninits = + do_dataflow(tcx, mir, id, attributes, &move_data, MaybeUninitializedLvals::new(tcx, mir)); + let flow_def_inits = + do_dataflow(tcx, mir, id, attributes, &move_data, DefinitelyInitializedLvals::new(tcx, mir)); if has_rustc_mir_with(attributes, "rustc_peek_maybe_init").is_some() { - dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &ctxt, &flow_inits); + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &move_data, &flow_inits); } if has_rustc_mir_with(attributes, "rustc_peek_maybe_uninit").is_some() { - dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &ctxt, &flow_uninits); + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &move_data, &flow_uninits); } if has_rustc_mir_with(attributes, "rustc_peek_definite_init").is_some() { - dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &ctxt, &flow_def_inits); + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &move_data, &flow_def_inits); } - let move_data = ctxt.2; if has_rustc_mir_with(attributes, "stop_after_dataflow").is_some() { bcx.tcx.sess.fatal("stop_after_dataflow ended compilation"); @@ -118,8 +116,8 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, node_id: ast::NodeId, attributes: &[ast::Attribute], - ctxt: BD::Ctxt, - bd: BD) -> (BD::Ctxt, DataflowResults) + ctxt: &BD::Ctxt, + bd: BD) -> DataflowResults where BD: BitDenotation + DataflowOperator, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> { use syntax::attr::AttrMetaMethods; @@ -156,7 +154,7 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pub struct MirBorrowckCtxtPreDataflow<'a, 'tcx: 'a, BD> - where BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation, BD::Ctxt: 'a+HasMoveData<'tcx> { node_id: ast::NodeId, flow_state: DataflowAnalysis<'a, 'tcx, BD>, From 0cf5f1023edcfe982e6e746a259a14930a847a4b Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 16:16:42 +0200 Subject: [PATCH 36/48] Replaced use of `interpret` method in `mir::dataflow::graphviz` with a client-provided closure. --- .../borrowck/mir/dataflow/graphviz.rs | 95 +++++++++++++++---- .../borrowck/mir/dataflow/mod.rs | 71 ++++---------- src/librustc_borrowck/borrowck/mir/mod.rs | 4 +- 3 files changed, 94 insertions(+), 76 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs index 5a8b3ec32062c..8b7f04287bc2c 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs @@ -21,15 +21,65 @@ use std::fs::File; use std::io; use std::io::prelude::*; use std::marker::PhantomData; +use std::mem; use std::path::Path; +use super::super::gather_moves::{MoveData}; use super::super::MirBorrowckCtxtPreDataflow; use bitslice::bits_to_string; +use indexed_set::{Idx, IdxSet}; use super::{BitDenotation, DataflowState}; -use super::{HasMoveData}; + +impl DataflowState { + fn each_bit(&self, ctxt: &O::Ctxt, words: &IdxSet, mut f: F) + where F: FnMut(O::Idx) { + //! Helper for iterating over the bits in a bitvector. + + let bits_per_block = self.operator.bits_per_block(ctxt); + let usize_bits: usize = mem::size_of::() * 8; + + for (word_index, &word) in words.words().iter().enumerate() { + if word != 0 { + let base_index = word_index * usize_bits; + for offset in 0..usize_bits { + let bit = 1 << offset; + if (word & bit) != 0 { + // NB: we round up the total number of bits + // that we store in any given bit set so that + // it is an even multiple of usize::BITS. This + // means that there may be some stray bits at + // the end that do not correspond to any + // actual value; that's why we first check + // that we are in range of bits_per_block. + let bit_index = base_index + offset as usize; + if bit_index >= bits_per_block { + return; + } else { + f(O::Idx::new(bit_index)); + } + } + } + } + } + } + + pub fn interpret_set<'c, P>(&self, + ctxt: &'c O::Ctxt, + words: &IdxSet, + render_idx: &P) + -> Vec<&'c Debug> + where P: for <'b> Fn(&'b O::Ctxt, O::Idx) -> &'b Debug + { + let mut v = Vec::new(); + self.each_bit(ctxt, words, |i| { + v.push(render_idx(ctxt, i)); + }); + v + } +} pub trait MirWithFlowState<'tcx> { - type BD: BitDenotation; + type BD: BitDenotation>; fn node_id(&self) -> NodeId; fn mir(&self) -> &Mir<'tcx>; fn analysis_ctxt(&self) -> &::Ctxt; @@ -37,7 +87,7 @@ pub trait MirWithFlowState<'tcx> { } impl<'a, 'tcx: 'a, BD> MirWithFlowState<'tcx> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where 'a, 'tcx: 'a, BD: BitDenotation, BD::Ctxt: HasMoveData<'tcx> + where 'a, 'tcx: 'a, BD: BitDenotation> { type BD = BD; fn node_id(&self) -> NodeId { self.node_id } @@ -46,19 +96,23 @@ impl<'a, 'tcx: 'a, BD> MirWithFlowState<'tcx> for MirBorrowckCtxtPreDataflow<'a, fn flow_state(&self) -> &DataflowState { &self.flow_state.flow_state } } -struct Graph<'a, 'tcx, MWF:'a> where MWF: MirWithFlowState<'tcx>, +struct Graph<'a, 'tcx, MWF:'a, P> where + MWF: MirWithFlowState<'tcx> { mbcx: &'a MWF, - phantom: PhantomData<&'tcx ()> + phantom: PhantomData<&'tcx ()>, + render_idx: P, } -pub fn print_borrowck_graph_to<'a, 'tcx, BD>( +pub fn print_borrowck_graph_to<'a, 'tcx, BD, P>( mbcx: &MirBorrowckCtxtPreDataflow<'a, 'tcx, BD>, - path: &Path) + path: &Path, + render_idx: P) -> io::Result<()> - where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx>, + where BD: BitDenotation>, BD::Bit: Debug, + P: for <'b> Fn(&'b BD::Ctxt, BD::Idx) -> &'b Debug { - let g = Graph { mbcx: mbcx, phantom: PhantomData }; + let g = Graph { mbcx: mbcx, phantom: PhantomData, render_idx: render_idx }; let mut v = Vec::new(); dot::render(&g, &mut v)?; debug!("print_borrowck_graph_to path: {} node_id: {}", @@ -76,8 +130,9 @@ fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec { (0..succ_len).map(|index| Edge { source: bb, index: index}).collect() } -impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> - where MWF: MirWithFlowState<'tcx>, ::Bit: Debug +impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P> + where MWF: MirWithFlowState<'tcx>, ::Bit: Debug, + P: for <'b> Fn(&'b ::Ctxt, ::Idx) -> &'b Debug, { type Node = Node; type Edge = Edge; @@ -136,10 +191,10 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> const BG_FLOWCONTENT: &'static str = r#"bgcolor="pink""#; const ALIGN_RIGHT: &'static str = r#"align="right""#; const FACE_MONOSPACE: &'static str = r#"FACE="Courier""#; - fn chunked_present_left(w: &mut W, - interpreted: &[&D], - chunk_size: usize) - -> io::Result<()> + fn chunked_present_left(w: &mut W, + interpreted: &[&Debug], + chunk_size: usize) + -> io::Result<()> { // This function may emit a sequence of 's, but it // always finishes with an (unfinished) @@ -171,7 +226,9 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> |w| { let ctxt = self.mbcx.analysis_ctxt(); let flow = self.mbcx.flow_state(); - let entry_interp = flow.interpret_set(ctxt, flow.sets.on_entry_set_for(i)); + let entry_interp = flow.interpret_set(ctxt, + flow.sets.on_entry_set_for(i), + &self.render_idx); chunked_present_left(w, &entry_interp[..], chunk_size)?; let bits_per_block = flow.sets.bits_per_block(); let entry = flow.sets.on_entry_set_for(i); @@ -186,8 +243,8 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> |w| { let ctxt = self.mbcx.analysis_ctxt(); let flow = self.mbcx.flow_state(); - let gen_interp = flow.interpret_set(ctxt, flow.sets.gen_set_for(i)); - let kill_interp = flow.interpret_set(ctxt, flow.sets.kill_set_for(i)); + let gen_interp = flow.interpret_set(ctxt, flow.sets.gen_set_for(i), &self.render_idx); + let kill_interp = flow.interpret_set(ctxt, flow.sets.kill_set_for(i), &self.render_idx); chunked_present_left(w, &gen_interp[..], chunk_size)?; let bits_per_block = flow.sets.bits_per_block(); { @@ -245,7 +302,7 @@ impl<'a, 'tcx, MWF> dot::Labeller<'a> for Graph<'a, 'tcx, MWF> } } -impl<'a, 'tcx, MWF> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF> +impl<'a, 'tcx, MWF, P> dot::GraphWalk<'a> for Graph<'a, 'tcx, MWF, P> where MWF: MirWithFlowState<'tcx> { type Node = Node; diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index e1470e62840da..fb8d2ec392243 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -31,20 +31,19 @@ mod graphviz; mod sanity_check; mod impls; -pub trait Dataflow { - fn dataflow(&mut self); +pub trait Dataflow { + fn dataflow

(&mut self, p: P) where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug; } -impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where BD: BitDenotation + DataflowOperator, +impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> + where BD: BitDenotation> + DataflowOperator, BD::Bit: Debug, - BD::Ctxt: HasMoveData<'tcx> { - fn dataflow(&mut self) { + fn dataflow

(&mut self, p: P) where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug { self.flow_state.build_sets(); - self.pre_dataflow_instrumentation().unwrap(); + self.pre_dataflow_instrumentation(|c,i| p(c,i)).unwrap(); self.flow_state.propagate(); - self.post_dataflow_instrumentation().unwrap(); + self.post_dataflow_instrumentation(|c,i| p(c,i)).unwrap(); } } @@ -142,21 +141,25 @@ fn dataflow_path(context: &str, prepost: &str, path: &str) -> PathBuf { } impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where BD: BitDenotation, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation>, BD::Bit: Debug { - fn pre_dataflow_instrumentation(&self) -> io::Result<()> { + fn pre_dataflow_instrumentation

(&self, p: P) -> io::Result<()> + where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug + { if let Some(ref path_str) = self.print_preflow_to { let path = dataflow_path(BD::name(), "preflow", path_str); - graphviz::print_borrowck_graph_to(self, &path) + graphviz::print_borrowck_graph_to(self, &path, p) } else { Ok(()) } } - fn post_dataflow_instrumentation(&self) -> io::Result<()> { + fn post_dataflow_instrumentation

(&self, p: P) -> io::Result<()> + where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug + { if let Some(ref path_str) = self.print_postflow_to { let path = dataflow_path(BD::name(), "postflow", path_str); - graphviz::print_borrowck_graph_to(self, &path) + graphviz::print_borrowck_graph_to(self, &path, p) } else{ Ok(()) } @@ -291,48 +294,6 @@ impl AllSets { } } -impl DataflowState { - fn each_bit(&self, ctxt: &O::Ctxt, words: &IdxSet, mut f: F) - where F: FnMut(usize) { - //! Helper for iterating over the bits in a bitvector. - - let bits_per_block = self.operator.bits_per_block(ctxt); - let usize_bits: usize = mem::size_of::() * 8; - - for (word_index, &word) in words.words().iter().enumerate() { - if word != 0 { - let base_index = word_index * usize_bits; - for offset in 0..usize_bits { - let bit = 1 << offset; - if (word & bit) != 0 { - // NB: we round up the total number of bits - // that we store in any given bit set so that - // it is an even multiple of usize::BITS. This - // means that there may be some stray bits at - // the end that do not correspond to any - // actual value; that's why we first check - // that we are in range of bits_per_block. - let bit_index = base_index + offset as usize; - if bit_index >= bits_per_block { - return; - } else { - f(bit_index); - } - } - } - } - } - } - - pub fn interpret_set<'c>(&self, ctxt: &'c O::Ctxt, words: &IdxSet) -> Vec<&'c O::Bit> { - let mut v = Vec::new(); - self.each_bit(ctxt, words, |i| { - v.push(self.operator.interpret(ctxt, i)); - }); - v - } -} - /// Parameterization for the precise form of data flow that is used. pub trait DataflowOperator: BitwiseOperator { /// Specifies the initial value for each bit in the `on_entry` set diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 54132cf0258da..92f8f91b93081 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -118,7 +118,7 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, attributes: &[ast::Attribute], ctxt: &BD::Ctxt, bd: BD) -> DataflowResults - where BD: BitDenotation + DataflowOperator, BD::Bit: Debug, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation> + DataflowOperator, BD::Bit: Debug { use syntax::attr::AttrMetaMethods; @@ -148,7 +148,7 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, flow_state: DataflowAnalysis::new(tcx, mir, ctxt, bd), }; - mbcx.dataflow(); + mbcx.dataflow(|move_data, i| &move_data.move_paths[i]); mbcx.flow_state.results() } From fdf80bc60cf77a5612e6b13e7f0c4fcf4cc59d0c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 16:46:19 +0200 Subject: [PATCH 37/48] Removed `type Bit` and `fn interpret` items from `trait BitDenotation`. Also got rid of the `trait HasMoveData`, since I am now just imposing the constraint that `BitDenotation>` where necessary instead. --- .../borrowck/mir/dataflow/graphviz.rs | 4 +- .../borrowck/mir/dataflow/impls.rs | 20 ++-------- .../borrowck/mir/dataflow/mod.rs | 39 +++++-------------- .../borrowck/mir/dataflow/sanity_check.rs | 13 +++---- src/librustc_borrowck/borrowck/mir/mod.rs | 7 +--- 5 files changed, 21 insertions(+), 62 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs index 8b7f04287bc2c..0d1443ede4101 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs @@ -109,7 +109,7 @@ pub fn print_borrowck_graph_to<'a, 'tcx, BD, P>( path: &Path, render_idx: P) -> io::Result<()> - where BD: BitDenotation>, BD::Bit: Debug, + where BD: BitDenotation>, P: for <'b> Fn(&'b BD::Ctxt, BD::Idx) -> &'b Debug { let g = Graph { mbcx: mbcx, phantom: PhantomData, render_idx: render_idx }; @@ -131,7 +131,7 @@ fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec { } impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P> - where MWF: MirWithFlowState<'tcx>, ::Bit: Debug, + where MWF: MirWithFlowState<'tcx>, P: for <'b> Fn(&'b ::Ctxt, ::Idx) -> &'b Debug, { type Node = Node; diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs index 480b890da6ad3..c580dc8551f26 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -12,7 +12,7 @@ use rustc::ty::TyCtxt; use rustc::mir::repr::{self, Mir}; use super::super::gather_moves::{Location}; -use super::super::gather_moves::{MoveData, MoveOut, MoveOutIndex, MovePath, MovePathIndex}; +use super::super::gather_moves::{MoveData, MoveOutIndex, MovePathIndex}; use super::super::DropFlagState; use super::super::drop_flag_effects_for_function_entry; use super::super::drop_flag_effects_for_location; @@ -226,15 +226,12 @@ impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; - type Bit = MovePath<'tcx>; type Ctxt = MoveData<'tcx>; fn name() -> &'static str { "maybe_init" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { ctxt.move_paths.len() } - fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.move_paths[MovePathIndex::new(idx)] - } + fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { drop_flag_effects_for_function_entry( @@ -288,15 +285,11 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; - type Bit = MovePath<'tcx>; type Ctxt = MoveData<'tcx>; fn name() -> &'static str { "maybe_uninit" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { ctxt.move_paths.len() } - fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.move_paths[MovePathIndex::new(idx)] - } // sets on_entry bits for Arg lvalues fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { @@ -354,15 +347,11 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; - type Bit = MovePath<'tcx>; type Ctxt = MoveData<'tcx>; fn name() -> &'static str { "definite_init" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { ctxt.move_paths.len() } - fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.move_paths[MovePathIndex::new(idx)] - } // sets on_entry bits for Arg lvalues fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) { @@ -419,15 +408,12 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { type Idx = MoveOutIndex; - type Bit = MoveOut; type Ctxt = MoveData<'tcx>; fn name() -> &'static str { "moving_out" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { ctxt.moves.len() } - fn interpret<'c>(&self, ctxt: &'c Self::Ctxt, idx: usize) -> &'c Self::Bit { - &ctxt.moves[idx] - } + fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) { // no move-statements have been executed prior to function // execution, so this method has no effect on `_sets`. diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index fb8d2ec392243..f91bd88d68f06 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -36,8 +36,7 @@ pub trait Dataflow { } impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where BD: BitDenotation> + DataflowOperator, - BD::Bit: Debug, + where BD: BitDenotation> + DataflowOperator { fn dataflow

(&mut self, p: P) where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug { self.flow_state.build_sets(); @@ -48,14 +47,14 @@ impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> } struct PropagationContext<'b, 'a: 'b, 'tcx: 'a, O> - where O: 'b + BitDenotation, O::Ctxt: 'a+HasMoveData<'tcx> + where O: 'b + BitDenotation, O::Ctxt: 'a { builder: &'b mut DataflowAnalysis<'a, 'tcx, O>, changed: bool, } impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> - where BD: BitDenotation + DataflowOperator, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation + DataflowOperator { fn propagate(&mut self) { let mut temp = OwnIdxSet::new_empty(self.flow_state.sets.bits_per_block); @@ -102,7 +101,7 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> } impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> - where BD: BitDenotation + DataflowOperator, BD::Ctxt: HasMoveData<'tcx> + where BD: BitDenotation + DataflowOperator { fn reset(&mut self, bits: &mut IdxSet) { let e = if BD::bottom_value() {!0} else {0}; @@ -141,7 +140,7 @@ fn dataflow_path(context: &str, prepost: &str, path: &str) -> PathBuf { } impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where BD: BitDenotation>, BD::Bit: Debug + where BD: BitDenotation> { fn pre_dataflow_instrumentation

(&self, p: P) -> io::Result<()> where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug @@ -182,19 +181,8 @@ impl Bits { } } -pub trait HasMoveData<'tcx> { - fn move_data(&self) -> &MoveData<'tcx>; -} - -impl<'tcx> HasMoveData<'tcx> for MoveData<'tcx> { - fn move_data(&self) -> &MoveData<'tcx> { self } -} -impl<'tcx, A, B> HasMoveData<'tcx> for (A, B, MoveData<'tcx>) { - fn move_data(&self) -> &MoveData<'tcx> { &self.2 } -} - pub struct DataflowAnalysis<'a, 'tcx: 'a, O> - where O: BitDenotation, O::Ctxt: 'a+HasMoveData<'tcx> + where O: BitDenotation, O::Ctxt: 'a { flow_state: DataflowState, mir: &'a Mir<'tcx>, @@ -202,7 +190,7 @@ pub struct DataflowAnalysis<'a, 'tcx: 'a, O> } impl<'a, 'tcx: 'a, O> DataflowAnalysis<'a, 'tcx, O> - where O: BitDenotation, O::Ctxt: HasMoveData<'tcx> + where O: BitDenotation { pub fn results(self) -> DataflowResults { DataflowResults(self.flow_state) @@ -301,9 +289,6 @@ pub trait DataflowOperator: BitwiseOperator { } pub trait BitDenotation { - /// Specifies what is represented by each bit in the dataflow bitvector. - type Bit; - /// Specifies what index type is used to access the bitvector. type Idx: Idx; @@ -322,10 +307,6 @@ pub trait BitDenotation { /// Size of each bitvector allocated for each block in the analysis. fn bits_per_block(&self, &Self::Ctxt) -> usize; - /// Provides the meaning of each entry in the dataflow bitvector. - /// (Mostly intended for use for better debug instrumentation.) - fn interpret<'a>(&self, &'a Self::Ctxt, idx: usize) -> &'a Self::Bit; - /// Mutates the block-sets (the flow sets for the given /// basic block) according to the effects that have been /// established *prior* to entering the start block. @@ -396,8 +377,7 @@ pub trait BitDenotation { } impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> - where D: BitDenotation + DataflowOperator, - D::Ctxt: HasMoveData<'tcx> + where D: BitDenotation + DataflowOperator { pub fn new(_tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &'a Mir<'tcx>, @@ -439,8 +419,7 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> } impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> - where D: BitDenotation + DataflowOperator, - D::Ctxt: HasMoveData<'tcx>, + where D: BitDenotation + DataflowOperator { /// Propagates the bits of `in_out` into all the successors of `bb`, /// using bitwise operator denoted by `self.operator`. diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index 932975b88084b..221768994942d 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -15,10 +15,9 @@ use syntax::codemap::Span; use rustc::ty::{self, TyCtxt}; use rustc::mir::repr::{self, Mir}; -use super::super::gather_moves::{MovePath, MovePathIndex}; +use super::super::gather_moves::{MoveData, MovePathIndex}; use super::BitDenotation; use super::DataflowResults; -use super::HasMoveData; /// This function scans `mir` for all calls to the intrinsic /// `rustc_peek` that have the expression form `rustc_peek(&expr)`. @@ -42,7 +41,7 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, _attributes: &[ast::Attribute], flow_ctxt: &O::Ctxt, results: &DataflowResults) - where O: BitDenotation, Idx=MovePathIndex>, O::Ctxt: HasMoveData<'tcx> + where O: BitDenotation, Idx=MovePathIndex> { debug!("sanity_check_via_rustc_peek id: {:?}", id); // FIXME: this is not DRY. Figure out way to abstract this and @@ -57,10 +56,10 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, - flow_ctxt: &O::Ctxt, + move_data: &O::Ctxt, results: &DataflowResults, bb: repr::BasicBlock) where - O: BitDenotation, Idx=MovePathIndex>, O::Ctxt: HasMoveData<'tcx> + O: BitDenotation, Idx=MovePathIndex> { let bb_data = mir.basic_block_data(bb); let &repr::BasicBlockData { ref statements, @@ -88,8 +87,6 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let mut gen = results.0.sets.gen_set_for(bb.index()).to_owned(); let mut kill = results.0.sets.kill_set_for(bb.index()).to_owned(); - let move_data = flow_ctxt.move_data(); - // Emulate effect of all statements in the block up to (but not // including) the borrow within `peek_arg_lval`. Do *not* include // call to `peek_arg_lval` itself (since we are peeking the state @@ -138,7 +135,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // reset GEN and KILL sets before emulating their effect. for e in sets.gen_set.words_mut() { *e = 0; } for e in sets.kill_set.words_mut() { *e = 0; } - results.0.operator.statement_effect(flow_ctxt, &mut sets, bb, j); + results.0.operator.statement_effect(move_data, &mut sets, bb, j); sets.on_entry.union(sets.gen_set); sets.on_entry.subtract(sets.kill_set); } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 92f8f91b93081..4b77e4c865b58 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -31,14 +31,11 @@ mod gather_moves; use self::dataflow::{BitDenotation}; use self::dataflow::{DataflowOperator}; use self::dataflow::{Dataflow, DataflowAnalysis, DataflowResults}; -use self::dataflow::{HasMoveData}; use self::dataflow::{MaybeInitializedLvals, MaybeUninitializedLvals}; use self::dataflow::{DefinitelyInitializedLvals}; use self::gather_moves::{MoveData, MovePathIndex, Location}; use self::gather_moves::{MovePathContent}; -use std::fmt::Debug; - fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option> { for attr in attrs { if attr.check_name("rustc_mir") { @@ -118,7 +115,7 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, attributes: &[ast::Attribute], ctxt: &BD::Ctxt, bd: BD) -> DataflowResults - where BD: BitDenotation> + DataflowOperator, BD::Bit: Debug + where BD: BitDenotation> + DataflowOperator { use syntax::attr::AttrMetaMethods; @@ -154,7 +151,7 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, pub struct MirBorrowckCtxtPreDataflow<'a, 'tcx: 'a, BD> - where BD: BitDenotation, BD::Ctxt: 'a+HasMoveData<'tcx> + where BD: BitDenotation, BD::Ctxt: 'a { node_id: ast::NodeId, flow_state: DataflowAnalysis<'a, 'tcx, BD>, From ad1294dbe7a5a812ca298206bec5dbf3e425e20d Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 23:03:52 +0200 Subject: [PATCH 38/48] threaded a `ty::ParameterEnvironment` for the current node id via the associated Ctxt item. used this to address a long-standing wart/bug in how filtering-out of values with type impl'ing `Copy` was done. --- .../borrowck/mir/dataflow/graphviz.rs | 8 ++-- .../borrowck/mir/dataflow/impls.rs | 37 ++++++++++--------- .../borrowck/mir/dataflow/mod.rs | 6 +-- .../borrowck/mir/dataflow/sanity_check.rs | 14 ++++--- src/librustc_borrowck/borrowck/mir/mod.rs | 35 +++++++++++------- 5 files changed, 56 insertions(+), 44 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs index 0d1443ede4101..588160fff692b 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs @@ -24,7 +24,7 @@ use std::marker::PhantomData; use std::mem; use std::path::Path; -use super::super::gather_moves::{MoveData}; +use super::super::MoveDataParamEnv; use super::super::MirBorrowckCtxtPreDataflow; use bitslice::bits_to_string; use indexed_set::{Idx, IdxSet}; @@ -79,7 +79,7 @@ impl DataflowState { } pub trait MirWithFlowState<'tcx> { - type BD: BitDenotation>; + type BD: BitDenotation>; fn node_id(&self) -> NodeId; fn mir(&self) -> &Mir<'tcx>; fn analysis_ctxt(&self) -> &::Ctxt; @@ -87,7 +87,7 @@ pub trait MirWithFlowState<'tcx> { } impl<'a, 'tcx: 'a, BD> MirWithFlowState<'tcx> for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where 'a, 'tcx: 'a, BD: BitDenotation> + where 'a, 'tcx: 'a, BD: BitDenotation> { type BD = BD; fn node_id(&self) -> NodeId { self.node_id } @@ -109,7 +109,7 @@ pub fn print_borrowck_graph_to<'a, 'tcx, BD, P>( path: &Path, render_idx: P) -> io::Result<()> - where BD: BitDenotation>, + where BD: BitDenotation>, P: for <'b> Fn(&'b BD::Ctxt, BD::Idx) -> &'b Debug { let g = Graph { mbcx: mbcx, phantom: PhantomData, render_idx: render_idx }; diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs index c580dc8551f26..e3435ed990506 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/impls.rs @@ -12,7 +12,8 @@ use rustc::ty::TyCtxt; use rustc::mir::repr::{self, Mir}; use super::super::gather_moves::{Location}; -use super::super::gather_moves::{MoveData, MoveOutIndex, MovePathIndex}; +use super::super::gather_moves::{MoveOutIndex, MovePathIndex}; +use super::super::MoveDataParamEnv; use super::super::DropFlagState; use super::super::drop_flag_effects_for_function_entry; use super::super::drop_flag_effects_for_location; @@ -226,10 +227,10 @@ impl<'a, 'tcx> DefinitelyInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; - type Ctxt = MoveData<'tcx>; + type Ctxt = MoveDataParamEnv<'tcx>; fn name() -> &'static str { "maybe_init" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.move_paths.len() + ctxt.move_data.move_paths.len() } fn start_block_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets) @@ -276,8 +277,8 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.rev_lookup.find(dest_lval); - on_all_children_bits(self.tcx, self.mir, ctxt, + let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval); + on_all_children_bits(self.tcx, self.mir, &ctxt.move_data, move_path_index, |mpi| { in_out.add(&mpi); }); } @@ -285,10 +286,10 @@ impl<'a, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; - type Ctxt = MoveData<'tcx>; + type Ctxt = MoveDataParamEnv<'tcx>; fn name() -> &'static str { "maybe_uninit" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.move_paths.len() + ctxt.move_data.move_paths.len() } // sets on_entry bits for Arg lvalues @@ -338,8 +339,8 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.rev_lookup.find(dest_lval); - on_all_children_bits(self.tcx, self.mir, ctxt, + let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval); + on_all_children_bits(self.tcx, self.mir, &ctxt.move_data, move_path_index, |mpi| { in_out.remove(&mpi); }); } @@ -347,10 +348,10 @@ impl<'a, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { type Idx = MovePathIndex; - type Ctxt = MoveData<'tcx>; + type Ctxt = MoveDataParamEnv<'tcx>; fn name() -> &'static str { "definite_init" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.move_paths.len() + ctxt.move_data.move_paths.len() } // sets on_entry bits for Arg lvalues @@ -399,8 +400,8 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { dest_lval: &repr::Lvalue) { // when a call returns successfully, that means we need to set // the bits for that dest_lval to 1 (initialized). - let move_path_index = ctxt.rev_lookup.find(dest_lval); - on_all_children_bits(self.tcx, self.mir, ctxt, + let move_path_index = ctxt.move_data.rev_lookup.find(dest_lval); + on_all_children_bits(self.tcx, self.mir, &ctxt.move_data, move_path_index, |mpi| { in_out.add(&mpi); }); } @@ -408,10 +409,10 @@ impl<'a, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'tcx> { impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { type Idx = MoveOutIndex; - type Ctxt = MoveData<'tcx>; + type Ctxt = MoveDataParamEnv<'tcx>; fn name() -> &'static str { "moving_out" } fn bits_per_block(&self, ctxt: &Self::Ctxt) -> usize { - ctxt.moves.len() + ctxt.move_data.moves.len() } fn start_block_effect(&self,_move_data: &Self::Ctxt, _sets: &mut BlockSets) { @@ -423,7 +424,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { sets: &mut BlockSets, bb: repr::BasicBlock, idx: usize) { - let (tcx, mir, move_data) = (self.tcx, self.mir, ctxt); + let (tcx, mir, move_data) = (self.tcx, self.mir, &ctxt.move_data); let stmt = &mir.basic_block_data(bb).statements[idx]; let loc_map = &move_data.loc_map; let path_map = &move_data.path_map; @@ -463,7 +464,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { bb: repr::BasicBlock, statements_len: usize) { - let (mir, move_data) = (self.mir, ctxt); + let (mir, move_data) = (self.mir, &ctxt.move_data); let term = mir.basic_block_data(bb).terminator.as_ref().unwrap(); let loc_map = &move_data.loc_map; let loc = Location { block: bb, index: statements_len }; @@ -482,7 +483,7 @@ impl<'a, 'tcx> BitDenotation for MovingOutStatements<'a, 'tcx> { _call_bb: repr::BasicBlock, _dest_bb: repr::BasicBlock, dest_lval: &repr::Lvalue) { - let move_data = ctxt; + let move_data = &ctxt.move_data; let move_path_index = move_data.rev_lookup.find(dest_lval); let bits_per_block = self.bits_per_block(ctxt); diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index f91bd88d68f06..c105a09970178 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -18,7 +18,7 @@ use std::path::PathBuf; use std::usize; use super::MirBorrowckCtxtPreDataflow; -use super::gather_moves::{MoveData}; +use super::MoveDataParamEnv; use bitslice::{bitwise, BitwiseOperator}; use indexed_set::{Idx, IdxSet, OwnIdxSet}; @@ -36,7 +36,7 @@ pub trait Dataflow { } impl<'a, 'tcx: 'a, BD> Dataflow for MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where BD: BitDenotation> + DataflowOperator + where BD: BitDenotation> + DataflowOperator { fn dataflow

(&mut self, p: P) where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug { self.flow_state.build_sets(); @@ -140,7 +140,7 @@ fn dataflow_path(context: &str, prepost: &str, path: &str) -> PathBuf { } impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> - where BD: BitDenotation> + where BD: BitDenotation> { fn pre_dataflow_instrumentation

(&self, p: P) -> io::Result<()> where P: Fn(&BD::Ctxt, BD::Idx) -> &Debug diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs index 221768994942d..74dc921b0bba5 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/sanity_check.rs @@ -15,7 +15,8 @@ use syntax::codemap::Span; use rustc::ty::{self, TyCtxt}; use rustc::mir::repr::{self, Mir}; -use super::super::gather_moves::{MoveData, MovePathIndex}; +use super::super::gather_moves::{MovePathIndex}; +use super::super::MoveDataParamEnv; use super::BitDenotation; use super::DataflowResults; @@ -41,7 +42,7 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, _attributes: &[ast::Attribute], flow_ctxt: &O::Ctxt, results: &DataflowResults) - where O: BitDenotation, Idx=MovePathIndex> + where O: BitDenotation, Idx=MovePathIndex> { debug!("sanity_check_via_rustc_peek id: {:?}", id); // FIXME: this is not DRY. Figure out way to abstract this and @@ -56,11 +57,12 @@ pub fn sanity_check_via_rustc_peek<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, - move_data: &O::Ctxt, + ctxt: &O::Ctxt, results: &DataflowResults, bb: repr::BasicBlock) where - O: BitDenotation, Idx=MovePathIndex> + O: BitDenotation, Idx=MovePathIndex> { + let move_data = &ctxt.move_data; let bb_data = mir.basic_block_data(bb); let &repr::BasicBlockData { ref statements, ref terminator, @@ -127,7 +129,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tcx.sess.span_err(span, msg); } } - + let lhs_mpi = move_data.rev_lookup.find(lvalue); debug!("rustc_peek: computing effect on lvalue: {:?} ({:?}) in stmt: {:?}", @@ -135,7 +137,7 @@ fn each_block<'a, 'tcx, O>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // reset GEN and KILL sets before emulating their effect. for e in sets.gen_set.words_mut() { *e = 0; } for e in sets.kill_set.words_mut() { *e = 0; } - results.0.operator.statement_effect(move_data, &mut sets, bb, j); + results.0.operator.statement_effect(ctxt, &mut sets, bb, j); sets.on_entry.union(sets.gen_set); sets.on_entry.subtract(sets.kill_set); } diff --git a/src/librustc_borrowck/borrowck/mir/mod.rs b/src/librustc_borrowck/borrowck/mir/mod.rs index 4b77e4c865b58..1b9d08bade7c4 100644 --- a/src/librustc_borrowck/borrowck/mir/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/mod.rs @@ -50,6 +50,11 @@ fn has_rustc_mir_with(attrs: &[ast::Attribute], name: &str) -> Option { + move_data: MoveData<'tcx>, + param_env: ty::ParameterEnvironment<'tcx>, +} + pub fn borrowck_mir<'a, 'tcx: 'a>( bcx: &mut BorrowckCtxt<'a, 'tcx>, fk: FnKind, @@ -72,21 +77,23 @@ pub fn borrowck_mir<'a, 'tcx: 'a>( let tcx = bcx.tcx; let move_data = MoveData::gather_moves(mir, tcx); + let param_env = ty::ParameterEnvironment::for_item(tcx, id); + let mdpe = MoveDataParamEnv { move_data: move_data, param_env: param_env }; let flow_inits = - do_dataflow(tcx, mir, id, attributes, &move_data, MaybeInitializedLvals::new(tcx, mir)); + do_dataflow(tcx, mir, id, attributes, &mdpe, MaybeInitializedLvals::new(tcx, mir)); let flow_uninits = - do_dataflow(tcx, mir, id, attributes, &move_data, MaybeUninitializedLvals::new(tcx, mir)); + do_dataflow(tcx, mir, id, attributes, &mdpe, MaybeUninitializedLvals::new(tcx, mir)); let flow_def_inits = - do_dataflow(tcx, mir, id, attributes, &move_data, DefinitelyInitializedLvals::new(tcx, mir)); + do_dataflow(tcx, mir, id, attributes, &mdpe, DefinitelyInitializedLvals::new(tcx, mir)); if has_rustc_mir_with(attributes, "rustc_peek_maybe_init").is_some() { - dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &move_data, &flow_inits); + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &mdpe, &flow_inits); } if has_rustc_mir_with(attributes, "rustc_peek_maybe_uninit").is_some() { - dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &move_data, &flow_uninits); + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &mdpe, &flow_uninits); } if has_rustc_mir_with(attributes, "rustc_peek_definite_init").is_some() { - dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &move_data, &flow_def_inits); + dataflow::sanity_check_via_rustc_peek(bcx.tcx, mir, id, attributes, &mdpe, &flow_def_inits); } if has_rustc_mir_with(attributes, "stop_after_dataflow").is_some() { @@ -97,7 +104,7 @@ pub fn borrowck_mir<'a, 'tcx: 'a>( bcx: bcx, mir: mir, node_id: id, - move_data: move_data, + move_data: mdpe.move_data, flow_inits: flow_inits, flow_uninits: flow_uninits, }; @@ -115,7 +122,7 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, attributes: &[ast::Attribute], ctxt: &BD::Ctxt, bd: BD) -> DataflowResults - where BD: BitDenotation> + DataflowOperator + where BD: BitDenotation> + DataflowOperator { use syntax::attr::AttrMetaMethods; @@ -145,7 +152,7 @@ fn do_dataflow<'a, 'tcx, BD>(tcx: TyCtxt<'a, 'tcx, 'tcx>, flow_state: DataflowAnalysis::new(tcx, mir, ctxt, bd), }; - mbcx.dataflow(|move_data, i| &move_data.move_paths[i]); + mbcx.dataflow(|ctxt, i| &ctxt.move_data.move_paths[i]); mbcx.flow_state.results() } @@ -253,10 +260,11 @@ fn on_all_children_bits<'a, 'tcx, F>( fn drop_flag_effects_for_function_entry<'a, 'tcx, F>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, - move_data: &MoveData<'tcx>, + ctxt: &MoveDataParamEnv<'tcx>, mut callback: F) where F: FnMut(MovePathIndex, DropFlagState) { + let move_data = &ctxt.move_data; for i in 0..(mir.arg_decls.len() as u32) { let lvalue = repr::Lvalue::Arg(i); let move_path_index = move_data.rev_lookup.find(&lvalue); @@ -269,11 +277,13 @@ fn drop_flag_effects_for_function_entry<'a, 'tcx, F>( fn drop_flag_effects_for_location<'a, 'tcx, F>( tcx: TyCtxt<'a, 'tcx, 'tcx>, mir: &Mir<'tcx>, - move_data: &MoveData<'tcx>, + ctxt: &MoveDataParamEnv<'tcx>, loc: Location, mut callback: F) where F: FnMut(MovePathIndex, DropFlagState) { + let move_data = &ctxt.move_data; + let param_env = &ctxt.param_env; debug!("drop_flag_effects_for_location({:?})", loc); // first, move out of the RHS @@ -284,8 +294,7 @@ fn drop_flag_effects_for_location<'a, 'tcx, F>( // don't move out of non-Copy things if let MovePathContent::Lvalue(ref lvalue) = move_data.move_paths[path].content { let ty = mir.lvalue_ty(tcx, lvalue).to_ty(tcx); - let empty_param_env = tcx.empty_parameter_environment(); - if !ty.moves_by_default(tcx, &empty_param_env, DUMMY_SP) { + if !ty.moves_by_default(tcx, param_env, DUMMY_SP) { continue; } } From a82676eefa4ed479042399a80d158012866444d6 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 23:08:07 +0200 Subject: [PATCH 39/48] placate tidy in `mir::dataflow::graphviz`. --- .../borrowck/mir/dataflow/graphviz.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs index 588160fff692b..63c11fb3545b0 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/graphviz.rs @@ -132,7 +132,9 @@ fn outgoing(mir: &Mir, bb: BasicBlock) -> Vec { impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P> where MWF: MirWithFlowState<'tcx>, - P: for <'b> Fn(&'b ::Ctxt, ::Idx) -> &'b Debug, + P: for <'b> Fn(&'b ::Ctxt, + ::Idx) + -> &'b Debug, { type Node = Node; type Edge = Edge; @@ -243,8 +245,10 @@ impl<'a, 'tcx, MWF, P> dot::Labeller<'a> for Graph<'a, 'tcx, MWF, P> |w| { let ctxt = self.mbcx.analysis_ctxt(); let flow = self.mbcx.flow_state(); - let gen_interp = flow.interpret_set(ctxt, flow.sets.gen_set_for(i), &self.render_idx); - let kill_interp = flow.interpret_set(ctxt, flow.sets.kill_set_for(i), &self.render_idx); + let gen_interp = + flow.interpret_set(ctxt, flow.sets.gen_set_for(i), &self.render_idx); + let kill_interp = + flow.interpret_set(ctxt, flow.sets.kill_set_for(i), &self.render_idx); chunked_present_left(w, &gen_interp[..], chunk_size)?; let bits_per_block = flow.sets.bits_per_block(); { From ac6ea445498c5447898d7bd5da2f2112b7641220 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 23:08:16 +0200 Subject: [PATCH 40/48] placate tidy in `mir::dataflow`. --- src/librustc_borrowck/borrowck/mir/dataflow/mod.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index c105a09970178..dad0c9251860e 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -121,9 +121,8 @@ impl<'b, 'a: 'b, 'tcx: 'a, BD> PropagationContext<'b, 'a, 'tcx, BD> in_out.union(sets.gen_set); in_out.subtract(sets.kill_set); } - builder.propagate_bits_into_graph_successors_of(in_out, - &mut self.changed, - (repr::BasicBlock::new(bb_idx), bb_data)); + builder.propagate_bits_into_graph_successors_of( + in_out, &mut self.changed, (repr::BasicBlock::new(bb_idx), bb_data)); } } } From fe49b41f833cd16c42e58ea9f9854aee1e33544c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 23:08:24 +0200 Subject: [PATCH 41/48] placate tidy in `mir::gather_moves`. --- src/librustc_borrowck/borrowck/mir/gather_moves.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index e196de46ee671..45fb98df65304 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -659,7 +659,8 @@ fn gather_moves<'a, 'tcx>(mir: &Mir<'tcx>, tcx: TyCtxt<'a, 'tcx, 'tcx>) -> MoveD debug!("gather_moves Return on_move_out_lval return {:?}", source); bb_ctxt.on_move_out_lval(SK::Return, &Lvalue::ReturnPointer, source); } else { - debug!("gather_moves Return on_move_out_lval assuming unreachable return {:?}", source); + debug!("gather_moves Return on_move_out_lval \ + assuming unreachable return {:?}", source); } } From 4412c7ab375452badb2b5052e6b78a87e2c36260 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Tue, 24 May 2016 23:08:34 +0200 Subject: [PATCH 42/48] placate tidy in compile-fail test. --- src/test/compile-fail/mir-dataflow/uninits-2.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/test/compile-fail/mir-dataflow/uninits-2.rs b/src/test/compile-fail/mir-dataflow/uninits-2.rs index a2869da7eb354..e0bf42534499c 100644 --- a/src/test/compile-fail/mir-dataflow/uninits-2.rs +++ b/src/test/compile-fail/mir-dataflow/uninits-2.rs @@ -19,7 +19,7 @@ use std::mem::{drop, replace}; struct S(i32); #[rustc_mir_borrowck] -#[rustc_mir(rustc_peek_maybe_uninit,stop_after_dataflow,borrowck_graphviz_postflow="/tmp/uninits-2.dot")] +#[rustc_mir(rustc_peek_maybe_uninit,stop_after_dataflow)] fn foo(x: &mut S) { // `x` is initialized here, so maybe-uninit bit is 0. From d9680f5b36b10cadf0de01b2ef6df0bec899e3b6 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 25 May 2016 14:52:40 +0200 Subject: [PATCH 43/48] Fix some comments. --- .../borrowck/mir/dataflow/mod.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index dad0c9251860e..da7a85c1a8a68 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -324,9 +324,9 @@ pub trait BitDenotation { /// "transfer-function" represnting the overall-effect of the /// block, represented via GEN and KILL sets. /// - /// The statement here is `idx_stmt.1`; `idx_stmt.0` is just - /// an identifying index: namely, the index of the statement - /// in the basic block. + /// The statement is identified as `bb_data[idx_stmt]`, where + /// `bb_data` is the sequence of statements identifed by `bb` in + /// the MIR. fn statement_effect(&self, ctxt: &Self::Ctxt, sets: &mut BlockSets, @@ -341,10 +341,6 @@ pub trait BitDenotation { /// "transfer-function" represnting the overall-effect of the /// block, represented via GEN and KILL sets. /// - /// The terminator here is `idx_term.1`; `idx_term.0` is just an - /// identifying index: namely, the number of statements in `bb` - /// itself. - /// /// The effects applied here cannot depend on which branch the /// terminator took. fn terminator_effect(&self, @@ -367,6 +363,11 @@ pub trait BitDenotation { /// flow-dependent, the current MIR cannot encode them via just /// GEN and KILL sets attached to the block, and so instead we add /// this extra machinery to represent the flow-dependent effect. + /// + /// FIXME: Right now this is a bit of a wart in the API. It might + /// be better to represent this as an additional gen- and + /// kill-sets associated with each edge coming out of the basic + /// block. fn propagate_call_return(&self, ctxt: &Self::Ctxt, in_out: &mut IdxSet, From 25f37fd6510d33fc2ad5900fd0a04d90abcb2086 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 25 May 2016 14:54:31 +0200 Subject: [PATCH 44/48] Add notes that data-structures should potentially move to different crate. --- src/librustc_borrowck/bitslice.rs | 3 +++ src/librustc_borrowck/indexed_set.rs | 2 ++ 2 files changed, 5 insertions(+) diff --git a/src/librustc_borrowck/bitslice.rs b/src/librustc_borrowck/bitslice.rs index 7a203b7f0b716..80fa86a007ed3 100644 --- a/src/librustc_borrowck/bitslice.rs +++ b/src/librustc_borrowck/bitslice.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// FIXME: move this to `rustc_data_structures` and potentially merge +// with `bitvec` there. + use std::mem; pub type Word = usize; diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs index bf5eb10b8ed53..feb01c635602e 100644 --- a/src/librustc_borrowck/indexed_set.rs +++ b/src/librustc_borrowck/indexed_set.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +// FIXME: move this to `rustc_data_structures` + use std::fmt; use std::marker::PhantomData; use std::mem; From 58f1a4949d348ec95362d737d8b5555491baee55 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 25 May 2016 15:41:49 +0200 Subject: [PATCH 45/48] remove unnecessary use of `indexed_set::Indexed` trait. --- src/librustc_borrowck/borrowck/mir/gather_moves.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/gather_moves.rs b/src/librustc_borrowck/borrowck/mir/gather_moves.rs index 45fb98df65304..48511cd5ebc91 100644 --- a/src/librustc_borrowck/borrowck/mir/gather_moves.rs +++ b/src/librustc_borrowck/borrowck/mir/gather_moves.rs @@ -20,7 +20,7 @@ use std::iter; use std::ops::Index; use super::abs_domain::{AbstractElem, Lift}; -use indexed_set::{Idx, Indexed}; +use indexed_set::{Idx}; // This submodule holds some newtype'd Index wrappers that are using // NonZero to ensure that Option occupies only a single word. @@ -60,14 +60,6 @@ mod indexes { pub use self::indexes::MovePathIndex; pub use self::indexes::MoveOutIndex; -impl<'tcx> Indexed for MovePath<'tcx> { - type Idx = MovePathIndex; -} - -impl Indexed for MoveOut { - type Idx = MoveOutIndex; -} - impl self::indexes::MoveOutIndex { pub fn move_path_index(&self, move_data: &MoveData) -> MovePathIndex { move_data.moves[self.idx()].path From a28771cc971a0b750984a3ebd67402e21892566c Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 25 May 2016 15:41:57 +0200 Subject: [PATCH 46/48] remove `indexed_set::Indexed` trait. --- src/librustc_borrowck/indexed_set.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs index feb01c635602e..5e842c1c5abaf 100644 --- a/src/librustc_borrowck/indexed_set.rs +++ b/src/librustc_borrowck/indexed_set.rs @@ -17,10 +17,6 @@ use std::ops::{Deref, DerefMut, Range}; use bitslice::{BitSlice, Word}; use bitslice::{bitwise, Union, Subtract}; -pub trait Indexed { - type Idx: Idx; -} - pub trait Idx: 'static { fn new(usize) -> Self; fn idx(&self) -> usize; From ad0e6adbb1453d7e0abe6d3f279b005036d4faa3 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 25 May 2016 15:53:59 +0200 Subject: [PATCH 47/48] fixes to `indexed_set`: add comments and fix `PhantomData` def'n. --- src/librustc_borrowck/indexed_set.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs index 5e842c1c5abaf..2625bd4300c27 100644 --- a/src/librustc_borrowck/indexed_set.rs +++ b/src/librustc_borrowck/indexed_set.rs @@ -17,13 +17,21 @@ use std::ops::{Deref, DerefMut, Range}; use bitslice::{BitSlice, Word}; use bitslice::{bitwise, Union, Subtract}; +/// Represents some newtyped `usize` wrapper. +/// +/// (purpose: avoid mixing indexes for different bitvector domains.) pub trait Idx: 'static { fn new(usize) -> Self; fn idx(&self) -> usize; } +/// Represents a set (or packed family of sets), of some element type +/// E, where each E is identified by some unique index type `T`. +/// +/// In other words, `T` is the type used to index into the bitvector +/// this type uses to represent the set of object it holds. pub struct OwnIdxSet { - _pd: PhantomData &T>, + _pd: PhantomData, bits: Vec, } @@ -40,8 +48,13 @@ impl Clone for OwnIdxSet { // requires a transmute relying on representation guarantees that may // not hold in the future. +/// Represents a set (or packed family of sets), of some element type +/// E, where each E is identified by some unique index type `T`. +/// +/// In other words, `T` is the type used to index into the bitslice +/// this type uses to represent the set of object it holds. pub struct IdxSet { - _pd: PhantomData &T>, + _pd: PhantomData, bits: [Word], } From df5c116250657daa98da84eebe1b44a495abf5c0 Mon Sep 17 00:00:00 2001 From: "Felix S. Klock II" Date: Wed, 25 May 2016 15:55:46 +0200 Subject: [PATCH 48/48] Alpha rename `OwnIdxSet` to `IdxSetBuf`. --- .../borrowck/mir/dataflow/mod.rs | 14 ++++++------ src/librustc_borrowck/indexed_set.rs | 22 +++++++++---------- 2 files changed, 18 insertions(+), 18 deletions(-) diff --git a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs index da7a85c1a8a68..b46b6c368a053 100644 --- a/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs +++ b/src/librustc_borrowck/borrowck/mir/dataflow/mod.rs @@ -21,7 +21,7 @@ use super::MirBorrowckCtxtPreDataflow; use super::MoveDataParamEnv; use bitslice::{bitwise, BitwiseOperator}; -use indexed_set::{Idx, IdxSet, OwnIdxSet}; +use indexed_set::{Idx, IdxSet, IdxSetBuf}; pub use self::sanity_check::sanity_check_via_rustc_peek; pub use self::impls::{MaybeInitializedLvals, MaybeUninitializedLvals}; @@ -57,7 +57,7 @@ impl<'a, 'tcx: 'a, BD> DataflowAnalysis<'a, 'tcx, BD> where BD: BitDenotation + DataflowOperator { fn propagate(&mut self) { - let mut temp = OwnIdxSet::new_empty(self.flow_state.sets.bits_per_block); + let mut temp = IdxSetBuf::new_empty(self.flow_state.sets.bits_per_block); let mut propcx = PropagationContext { builder: self, changed: true, @@ -167,7 +167,7 @@ impl<'a, 'tcx: 'a, BD> MirBorrowckCtxtPreDataflow<'a, 'tcx, BD> /// Maps each block to a set of bits #[derive(Debug)] struct Bits { - bits: OwnIdxSet, + bits: IdxSetBuf, } impl Clone for Bits { @@ -175,7 +175,7 @@ impl Clone for Bits { } impl Bits { - fn new(bits: OwnIdxSet) -> Self { + fn new(bits: IdxSetBuf) -> Self { Bits { bits: bits } } } @@ -393,11 +393,11 @@ impl<'a, 'tcx: 'a, D> DataflowAnalysis<'a, 'tcx, D> let num_blocks = mir.basic_blocks.len(); let num_overall = num_blocks * bits_per_block; - let zeroes = Bits::new(OwnIdxSet::new_empty(num_overall)); + let zeroes = Bits::new(IdxSetBuf::new_empty(num_overall)); let on_entry = Bits::new(if D::bottom_value() { - OwnIdxSet::new_filled(num_overall) + IdxSetBuf::new_filled(num_overall) } else { - OwnIdxSet::new_empty(num_overall) + IdxSetBuf::new_empty(num_overall) }); DataflowAnalysis { diff --git a/src/librustc_borrowck/indexed_set.rs b/src/librustc_borrowck/indexed_set.rs index 2625bd4300c27..3fee1dbc05660 100644 --- a/src/librustc_borrowck/indexed_set.rs +++ b/src/librustc_borrowck/indexed_set.rs @@ -30,21 +30,21 @@ pub trait Idx: 'static { /// /// In other words, `T` is the type used to index into the bitvector /// this type uses to represent the set of object it holds. -pub struct OwnIdxSet { +pub struct IdxSetBuf { _pd: PhantomData, bits: Vec, } -impl Clone for OwnIdxSet { +impl Clone for IdxSetBuf { fn clone(&self) -> Self { - OwnIdxSet { _pd: PhantomData, bits: self.bits.clone() } + IdxSetBuf { _pd: PhantomData, bits: self.bits.clone() } } } // pnkfelix wants to have this be `IdxSet([Word]) and then pass // around `&mut IdxSet` or `&IdxSet`. // -// WARNING: Mapping a `&OwnIdxSet` to `&IdxSet` (at least today) +// WARNING: Mapping a `&IdxSetBuf` to `&IdxSet` (at least today) // requires a transmute relying on representation guarantees that may // not hold in the future. @@ -58,7 +58,7 @@ pub struct IdxSet { bits: [Word], } -impl fmt::Debug for OwnIdxSet { +impl fmt::Debug for IdxSetBuf { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) } } @@ -66,11 +66,11 @@ impl fmt::Debug for IdxSet { fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { self.bits.fmt(w) } } -impl OwnIdxSet { +impl IdxSetBuf { fn new(init: Word, universe_size: usize) -> Self { let bits_per_word = mem::size_of::() * 8; let num_words = (universe_size + (bits_per_word - 1)) / bits_per_word; - OwnIdxSet { + IdxSetBuf { _pd: Default::default(), bits: vec![init; num_words], } @@ -97,22 +97,22 @@ impl IdxSet { } } -impl Deref for OwnIdxSet { +impl Deref for IdxSetBuf { type Target = IdxSet; fn deref(&self) -> &IdxSet { unsafe { IdxSet::from_slice(&self.bits[..]) } } } -impl DerefMut for OwnIdxSet { +impl DerefMut for IdxSetBuf { fn deref_mut(&mut self) -> &mut IdxSet { unsafe { IdxSet::from_slice_mut(&mut self.bits[..]) } } } impl IdxSet { - pub fn to_owned(&self) -> OwnIdxSet { - OwnIdxSet { + pub fn to_owned(&self) -> IdxSetBuf { + IdxSetBuf { _pd: Default::default(), bits: self.bits.to_owned(), }