2017-06-26 14:57:26 +02:00
|
|
|
//! Dataflow analyses are built upon some interpretation of the
|
|
|
|
//! bitvectors attached to each basic block, represented via a
|
|
|
|
//! zero-sized structure.
|
|
|
|
|
2016-05-24 13:26:54 +02:00
|
|
|
use rustc::ty::TyCtxt;
|
2019-05-17 23:55:04 +02:00
|
|
|
use rustc::mir::{self, Body, Location};
|
2018-09-17 13:52:35 +10:00
|
|
|
use rustc_data_structures::bit_set::{BitSet, BitSetOperator};
|
2017-11-08 18:32:08 +03:00
|
|
|
use rustc_data_structures::indexed_vec::Idx;
|
2016-05-24 13:26:54 +02:00
|
|
|
|
2017-06-26 14:57:26 +02:00
|
|
|
use super::MoveDataParamEnv;
|
2018-04-06 15:18:01 -04:00
|
|
|
|
2019-02-08 06:28:15 +09:00
|
|
|
use crate::util::elaborate_drops::DropFlagState;
|
2016-05-24 13:26:54 +02:00
|
|
|
|
2019-04-03 19:30:18 +01:00
|
|
|
use super::move_paths::{HasMoveData, MoveData, MovePathIndex, InitIndex, InitKind};
|
2017-11-27 15:08:11 +01:00
|
|
|
use super::{BitDenotation, BlockSets, InitialFlow};
|
2016-05-24 13:26:54 +02:00
|
|
|
|
2017-06-26 14:57:26 +02:00
|
|
|
use super::drop_flag_effects_for_function_entry;
|
|
|
|
use super::drop_flag_effects_for_location;
|
2018-08-30 18:54:32 -03:00
|
|
|
use super::on_lookup_result_bits;
|
2016-05-24 13:26:54 +02:00
|
|
|
|
2017-09-10 22:34:56 +02:00
|
|
|
mod storage_liveness;
|
|
|
|
|
|
|
|
pub use self::storage_liveness::*;
|
|
|
|
|
2018-01-29 08:48:56 +01:00
|
|
|
mod borrowed_locals;
|
|
|
|
|
|
|
|
pub use self::borrowed_locals::*;
|
|
|
|
|
2017-07-03 18:40:20 +02:00
|
|
|
pub(super) mod borrows;
|
|
|
|
|
2018-01-29 01:49:29 +02:00
|
|
|
/// `MaybeInitializedPlaces` tracks all places that might be
|
2016-05-24 13:26:54 +02:00
|
|
|
/// 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}
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2018-01-29 01:49:29 +02:00
|
|
|
/// To determine whether a place *must* be initialized at a
|
2016-05-24 13:26:54 +02:00
|
|
|
/// particular control-flow point, one can take the set-difference
|
2018-01-29 01:49:29 +02:00
|
|
|
/// between this data and the data from `MaybeUninitializedPlaces` at the
|
2016-05-24 13:26:54 +02:00
|
|
|
/// corresponding control-flow point.
|
|
|
|
///
|
|
|
|
/// Similarly, at a given `drop` statement, the set-intersection
|
2018-01-29 01:49:29 +02:00
|
|
|
/// between this data and `MaybeUninitializedPlaces` yields the set of
|
|
|
|
/// places that would require a dynamic drop-flag at that statement.
|
2019-06-14 19:39:39 +03:00
|
|
|
pub struct MaybeInitializedPlaces<'a, 'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &'a Body<'tcx>,
|
2019-06-14 00:48:52 +03:00
|
|
|
mdpe: &'a MoveDataParamEnv<'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
|
2019-06-14 01:32:15 +03:00
|
|
|
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
|
2019-06-03 18:26:48 -04:00
|
|
|
MaybeInitializedPlaces { tcx: tcx, body: body, mdpe: mdpe }
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> HasMoveData<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
|
2016-12-26 09:40:15 -05:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
2018-01-29 01:49:29 +02:00
|
|
|
/// `MaybeUninitializedPlaces` tracks all places that might be
|
2016-05-24 13:26:54 +02:00
|
|
|
/// 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}
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2018-01-29 01:49:29 +02:00
|
|
|
/// To determine whether a place *must* be uninitialized at a
|
2016-05-24 13:26:54 +02:00
|
|
|
/// particular control-flow point, one can take the set-difference
|
2018-01-29 01:49:29 +02:00
|
|
|
/// between this data and the data from `MaybeInitializedPlaces` at the
|
2016-05-24 13:26:54 +02:00
|
|
|
/// corresponding control-flow point.
|
|
|
|
///
|
|
|
|
/// Similarly, at a given `drop` statement, the set-intersection
|
2018-01-29 01:49:29 +02:00
|
|
|
/// between this data and `MaybeInitializedPlaces` yields the set of
|
|
|
|
/// places that would require a dynamic drop-flag at that statement.
|
2019-06-14 19:39:39 +03:00
|
|
|
pub struct MaybeUninitializedPlaces<'a, 'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &'a Body<'tcx>,
|
2019-06-14 00:48:52 +03:00
|
|
|
mdpe: &'a MoveDataParamEnv<'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
|
2019-06-14 01:32:15 +03:00
|
|
|
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
|
2019-06-03 18:26:48 -04:00
|
|
|
MaybeUninitializedPlaces { tcx: tcx, body: body, mdpe: mdpe }
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> HasMoveData<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
|
2016-12-26 09:40:15 -05:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
2018-01-29 01:49:29 +02:00
|
|
|
/// `DefinitelyInitializedPlaces` tracks all places that are definitely
|
2016-05-24 13:26:54 +02:00
|
|
|
/// 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) { // 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 }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
///
|
2018-01-29 01:49:29 +02:00
|
|
|
/// To determine whether a place *may* be uninitialized at a
|
2016-05-24 13:26:54 +02:00
|
|
|
/// particular control-flow point, one can take the set-complement
|
|
|
|
/// of this data.
|
|
|
|
///
|
|
|
|
/// Similarly, at a given `drop` statement, the set-difference between
|
2018-01-29 01:49:29 +02:00
|
|
|
/// this data and `MaybeInitializedPlaces` yields the set of places
|
2016-05-24 13:26:54 +02:00
|
|
|
/// that would require a dynamic drop-flag at that statement.
|
2019-06-14 19:39:39 +03:00
|
|
|
pub struct DefinitelyInitializedPlaces<'a, 'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &'a Body<'tcx>,
|
2019-06-14 00:48:52 +03:00
|
|
|
mdpe: &'a MoveDataParamEnv<'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
|
|
|
|
2019-06-14 19:39:39 +03:00
|
|
|
impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
|
2019-06-14 01:32:15 +03:00
|
|
|
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
|
2019-06-03 18:26:48 -04:00
|
|
|
DefinitelyInitializedPlaces { tcx: tcx, body: body, mdpe: mdpe }
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
|
2019-06-14 19:39:39 +03:00
|
|
|
impl<'a, 'tcx> HasMoveData<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
|
2016-12-26 09:40:15 -05:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
2018-01-29 01:49:29 +02:00
|
|
|
/// `EverInitializedPlaces` tracks all places that might have ever been
|
2017-11-27 08:06:36 +00:00
|
|
|
/// initialized upon reaching a particular point in the control flow
|
|
|
|
/// for a function, without an intervening `Storage Dead`.
|
|
|
|
///
|
|
|
|
/// This dataflow is used to determine if an immutable local variable may
|
|
|
|
/// be assigned to.
|
|
|
|
///
|
|
|
|
/// 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) { // ever-init:
|
|
|
|
/// // { }
|
|
|
|
/// let a = S; let b = S; let c; let d; // {a, b }
|
|
|
|
///
|
|
|
|
/// if pred {
|
|
|
|
/// drop(a); // {a, b, }
|
|
|
|
/// b = S; // {a, b, }
|
|
|
|
///
|
|
|
|
/// } else {
|
|
|
|
/// drop(b); // {a, b, }
|
|
|
|
/// d = S; // {a, b, d }
|
|
|
|
///
|
|
|
|
/// } // {a, b, d }
|
|
|
|
///
|
|
|
|
/// c = S; // {a, b, c, d }
|
|
|
|
/// }
|
|
|
|
/// ```
|
2019-06-14 19:39:39 +03:00
|
|
|
pub struct EverInitializedPlaces<'a, 'tcx> {
|
2019-06-14 00:48:52 +03:00
|
|
|
tcx: TyCtxt<'tcx>,
|
2019-06-03 18:26:48 -04:00
|
|
|
body: &'a Body<'tcx>,
|
2019-06-14 00:48:52 +03:00
|
|
|
mdpe: &'a MoveDataParamEnv<'tcx>,
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
|
|
|
|
2019-06-14 19:39:39 +03:00
|
|
|
impl<'a, 'tcx> EverInitializedPlaces<'a, 'tcx> {
|
2019-06-14 01:32:15 +03:00
|
|
|
pub fn new(tcx: TyCtxt<'tcx>, body: &'a Body<'tcx>, mdpe: &'a MoveDataParamEnv<'tcx>) -> Self {
|
2019-06-03 18:26:48 -04:00
|
|
|
EverInitializedPlaces { tcx: tcx, body: body, mdpe: mdpe }
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> HasMoveData<'tcx> for EverInitializedPlaces<'a, 'tcx> {
|
2017-11-27 08:06:36 +00:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> MaybeInitializedPlaces<'a, 'tcx> {
|
2019-02-08 06:28:15 +09:00
|
|
|
fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
|
2016-05-24 13:26:54 +02:00
|
|
|
state: DropFlagState)
|
|
|
|
{
|
|
|
|
match state {
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
DropFlagState::Absent => sets.kill(path),
|
|
|
|
DropFlagState::Present => sets.gen(path),
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> MaybeUninitializedPlaces<'a, 'tcx> {
|
2019-02-08 06:28:15 +09:00
|
|
|
fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
|
2016-05-24 13:26:54 +02:00
|
|
|
state: DropFlagState)
|
|
|
|
{
|
|
|
|
match state {
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
DropFlagState::Absent => sets.gen(path),
|
|
|
|
DropFlagState::Present => sets.kill(path),
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> DefinitelyInitializedPlaces<'a, 'tcx> {
|
2019-02-08 06:28:15 +09:00
|
|
|
fn update_bits(sets: &mut BlockSets<'_, MovePathIndex>, path: MovePathIndex,
|
2016-05-24 13:26:54 +02:00
|
|
|
state: DropFlagState)
|
|
|
|
{
|
|
|
|
match state {
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
DropFlagState::Absent => sets.kill(path),
|
|
|
|
DropFlagState::Present => sets.gen(path),
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> BitDenotation<'tcx> for MaybeInitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
type Idx = MovePathIndex;
|
|
|
|
fn name() -> &'static str { "maybe_init" }
|
2016-12-26 09:40:15 -05:00
|
|
|
fn bits_per_block(&self) -> usize {
|
|
|
|
self.move_data().move_paths.len()
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
2016-05-24 16:46:19 +02:00
|
|
|
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
|
2016-05-24 13:26:54 +02:00
|
|
|
drop_flag_effects_for_function_entry(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| {
|
|
|
|
assert!(s == DropFlagState::Present);
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
entry_set.insert(path);
|
2016-05-24 13:26:54 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn statement_effect(&self,
|
2019-02-08 06:28:15 +09:00
|
|
|
sets: &mut BlockSets<'_, MovePathIndex>,
|
2017-07-03 17:58:19 +02:00
|
|
|
location: Location)
|
2016-05-24 13:26:54 +02:00
|
|
|
{
|
|
|
|
drop_flag_effects_for_location(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2017-07-03 17:58:19 +02:00
|
|
|
location,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| Self::update_bits(sets, path, s)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn terminator_effect(&self,
|
2019-02-08 06:28:15 +09:00
|
|
|
sets: &mut BlockSets<'_, MovePathIndex>,
|
2017-07-03 17:58:19 +02:00
|
|
|
location: Location)
|
2016-05-24 13:26:54 +02:00
|
|
|
{
|
|
|
|
drop_flag_effects_for_location(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2017-07-03 17:58:19 +02:00
|
|
|
location,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| Self::update_bits(sets, path, s)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-12-17 17:26:24 +01:00
|
|
|
fn propagate_call_return(
|
|
|
|
&self,
|
|
|
|
in_out: &mut BitSet<MovePathIndex>,
|
|
|
|
_call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
|
|
|
dest_place: &mir::Place<'tcx>,
|
|
|
|
) {
|
2016-05-24 13:26:54 +02:00
|
|
|
// when a call returns successfully, that means we need to set
|
2017-12-01 14:39:51 +02:00
|
|
|
// the bits for that dest_place to 1 (initialized).
|
2019-06-03 18:26:48 -04:00
|
|
|
on_lookup_result_bits(self.tcx, self.body, self.move_data(),
|
2017-12-01 14:39:51 +02:00
|
|
|
self.move_data().rev_lookup.find(dest_place),
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
|mpi| { in_out.insert(mpi); });
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> BitDenotation<'tcx> for MaybeUninitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
type Idx = MovePathIndex;
|
|
|
|
fn name() -> &'static str { "maybe_uninit" }
|
2016-12-26 09:40:15 -05:00
|
|
|
fn bits_per_block(&self) -> usize {
|
|
|
|
self.move_data().move_paths.len()
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
|
2017-12-01 14:39:51 +02:00
|
|
|
// sets on_entry bits for Arg places
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
|
2016-05-24 13:26:54 +02:00
|
|
|
// set all bits to 1 (uninit) before gathering counterevidence
|
Improve handling of type bounds in `bit_set.rs`.
Currently, `BitSet` doesn't actually know its own domain size; it just
knows how many words it contains. To improve things, this commit makes
the following changes.
- It changes `BitSet` and `SparseBitSet` to store their own domain size,
and do more precise bounds and same-size checks with it. It also
changes the signature of `BitSet::to_string()` (and puts it within
`impl ToString`) now that the domain size need not be passed in from
outside.
- It uses `derive(RustcDecodable, RustcEncodable)` for `BitSet`. This
required adding code to handle `PhantomData` in `libserialize`.
- As a result, it removes the domain size from `HybridBitSet`, making a
lot of that code nicer.
- Both set_up_to() and clear_above() were overly general, working with
arbitrary sizes when they are only needed for the domain size. The
commit removes the former, degeneralizes the latter, and removes the
(overly general) tests.
- Changes `GrowableBitSet::grow()` to `ensure()`, fixing a bug where a
(1-based) domain size was confused with a (0-based) element index.
- Changes `BitMatrix` to store its row count, and do more precise bounds
checks with it.
- Changes `ty_params` in `select.rs` from a `BitSet` to a
`GrowableBitSet` because it repeatedly failed the new, more precise
bounds checks. (Changing the type was simpler than computing an
accurate domain size.)
- Various other minor improvements.
2018-09-18 17:03:24 +10:00
|
|
|
assert!(self.bits_per_block() == entry_set.domain_size());
|
|
|
|
entry_set.insert_all();
|
2016-05-24 13:26:54 +02:00
|
|
|
|
|
|
|
drop_flag_effects_for_function_entry(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| {
|
|
|
|
assert!(s == DropFlagState::Present);
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
entry_set.remove(path);
|
2016-05-24 13:26:54 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn statement_effect(&self,
|
2019-02-08 06:28:15 +09:00
|
|
|
sets: &mut BlockSets<'_, MovePathIndex>,
|
2017-07-03 17:58:19 +02:00
|
|
|
location: Location)
|
2016-05-24 13:26:54 +02:00
|
|
|
{
|
|
|
|
drop_flag_effects_for_location(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2017-07-03 17:58:19 +02:00
|
|
|
location,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| Self::update_bits(sets, path, s)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn terminator_effect(&self,
|
2019-02-08 06:28:15 +09:00
|
|
|
sets: &mut BlockSets<'_, MovePathIndex>,
|
2017-07-03 17:58:19 +02:00
|
|
|
location: Location)
|
2016-05-24 13:26:54 +02:00
|
|
|
{
|
|
|
|
drop_flag_effects_for_location(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2017-07-03 17:58:19 +02:00
|
|
|
location,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| Self::update_bits(sets, path, s)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-12-17 17:26:24 +01:00
|
|
|
fn propagate_call_return(
|
|
|
|
&self,
|
|
|
|
in_out: &mut BitSet<MovePathIndex>,
|
|
|
|
_call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
|
|
|
dest_place: &mir::Place<'tcx>,
|
|
|
|
) {
|
2016-05-24 13:26:54 +02:00
|
|
|
// when a call returns successfully, that means we need to set
|
2017-12-01 14:39:51 +02:00
|
|
|
// the bits for that dest_place to 0 (initialized).
|
2019-06-03 18:26:48 -04:00
|
|
|
on_lookup_result_bits(self.tcx, self.body, self.move_data(),
|
2017-12-01 14:39:51 +02:00
|
|
|
self.move_data().rev_lookup.find(dest_place),
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
|mpi| { in_out.remove(mpi); });
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> BitDenotation<'tcx> for DefinitelyInitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
type Idx = MovePathIndex;
|
|
|
|
fn name() -> &'static str { "definite_init" }
|
2016-12-26 09:40:15 -05:00
|
|
|
fn bits_per_block(&self) -> usize {
|
|
|
|
self.move_data().move_paths.len()
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
|
2017-12-01 14:39:51 +02:00
|
|
|
// sets on_entry bits for Arg places
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
fn start_block_effect(&self, entry_set: &mut BitSet<MovePathIndex>) {
|
2018-04-02 00:14:44 +03:00
|
|
|
entry_set.clear();
|
2016-05-24 13:26:54 +02:00
|
|
|
|
|
|
|
drop_flag_effects_for_function_entry(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| {
|
|
|
|
assert!(s == DropFlagState::Present);
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
entry_set.insert(path);
|
2016-05-24 13:26:54 +02:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn statement_effect(&self,
|
2019-02-08 06:28:15 +09:00
|
|
|
sets: &mut BlockSets<'_, MovePathIndex>,
|
2017-07-03 17:58:19 +02:00
|
|
|
location: Location)
|
2016-05-24 13:26:54 +02:00
|
|
|
{
|
|
|
|
drop_flag_effects_for_location(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2017-07-03 17:58:19 +02:00
|
|
|
location,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| Self::update_bits(sets, path, s)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
|
|
|
fn terminator_effect(&self,
|
2019-02-08 06:28:15 +09:00
|
|
|
sets: &mut BlockSets<'_, MovePathIndex>,
|
2017-07-03 17:58:19 +02:00
|
|
|
location: Location)
|
2016-05-24 13:26:54 +02:00
|
|
|
{
|
|
|
|
drop_flag_effects_for_location(
|
2019-06-03 18:26:48 -04:00
|
|
|
self.tcx, self.body, self.mdpe,
|
2017-07-03 17:58:19 +02:00
|
|
|
location,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| Self::update_bits(sets, path, s)
|
|
|
|
)
|
|
|
|
}
|
|
|
|
|
2018-12-17 17:26:24 +01:00
|
|
|
fn propagate_call_return(
|
|
|
|
&self,
|
|
|
|
in_out: &mut BitSet<MovePathIndex>,
|
|
|
|
_call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
|
|
|
dest_place: &mir::Place<'tcx>,
|
|
|
|
) {
|
2016-05-24 13:26:54 +02:00
|
|
|
// when a call returns successfully, that means we need to set
|
2017-12-01 14:39:51 +02:00
|
|
|
// the bits for that dest_place to 1 (initialized).
|
2019-06-03 18:26:48 -04:00
|
|
|
on_lookup_result_bits(self.tcx, self.body, self.move_data(),
|
2017-12-01 14:39:51 +02:00
|
|
|
self.move_data().rev_lookup.find(dest_place),
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
|mpi| { in_out.insert(mpi); });
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> BitDenotation<'tcx> for EverInitializedPlaces<'a, 'tcx> {
|
2017-11-27 08:06:36 +00:00
|
|
|
type Idx = InitIndex;
|
|
|
|
fn name() -> &'static str { "ever_init" }
|
|
|
|
fn bits_per_block(&self) -> usize {
|
|
|
|
self.move_data().inits.len()
|
|
|
|
}
|
|
|
|
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
fn start_block_effect(&self, entry_set: &mut BitSet<InitIndex>) {
|
2019-06-03 18:26:48 -04:00
|
|
|
for arg_init in 0..self.body.arg_count {
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
entry_set.insert(InitIndex::new(arg_init));
|
2017-12-07 20:06:55 +02:00
|
|
|
}
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
2017-12-07 20:06:55 +02:00
|
|
|
|
2017-11-27 08:06:36 +00:00
|
|
|
fn statement_effect(&self,
|
2019-02-08 06:28:15 +09:00
|
|
|
sets: &mut BlockSets<'_, InitIndex>,
|
2017-11-27 08:06:36 +00:00
|
|
|
location: Location) {
|
2019-06-03 18:26:48 -04:00
|
|
|
let (_, body, move_data) = (self.tcx, self.body, self.move_data());
|
|
|
|
let stmt = &body[location.block].statements[location.statement_index];
|
2017-11-27 08:06:36 +00:00
|
|
|
let init_path_map = &move_data.init_path_map;
|
|
|
|
let init_loc_map = &move_data.init_loc_map;
|
|
|
|
let rev_lookup = &move_data.rev_lookup;
|
|
|
|
|
|
|
|
debug!("statement {:?} at loc {:?} initializes move_indexes {:?}",
|
|
|
|
stmt, location, &init_loc_map[location]);
|
2017-12-04 01:00:46 +02:00
|
|
|
sets.gen_all(&init_loc_map[location]);
|
2017-11-27 08:06:36 +00:00
|
|
|
|
|
|
|
match stmt.kind {
|
2019-04-03 19:30:18 +01:00
|
|
|
mir::StatementKind::StorageDead(local) => {
|
|
|
|
// End inits for StorageDead, so that an immutable variable can
|
|
|
|
// be reinitialized on the next iteration of the loop.
|
|
|
|
let move_path_index = rev_lookup.find_local(local);
|
|
|
|
debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
|
|
|
|
stmt, location, &init_path_map[move_path_index]);
|
|
|
|
sets.kill_all(&init_path_map[move_path_index]);
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn terminator_effect(&self,
|
2019-02-08 06:28:15 +09:00
|
|
|
sets: &mut BlockSets<'_, InitIndex>,
|
2017-11-27 08:06:36 +00:00
|
|
|
location: Location)
|
|
|
|
{
|
2019-06-03 18:26:48 -04:00
|
|
|
let (body, move_data) = (self.body, self.move_data());
|
|
|
|
let term = body[location.block].terminator();
|
2017-11-27 08:06:36 +00:00
|
|
|
let init_loc_map = &move_data.init_loc_map;
|
|
|
|
debug!("terminator {:?} at loc {:?} initializes move_indexes {:?}",
|
|
|
|
term, location, &init_loc_map[location]);
|
2017-12-04 01:00:46 +02:00
|
|
|
sets.gen_all(
|
|
|
|
init_loc_map[location].iter().filter(|init_index| {
|
|
|
|
move_data.inits[**init_index].kind != InitKind::NonPanicPathOnly
|
|
|
|
})
|
|
|
|
);
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
|
|
|
|
2018-12-17 17:26:24 +01:00
|
|
|
fn propagate_call_return(
|
|
|
|
&self,
|
|
|
|
in_out: &mut BitSet<InitIndex>,
|
|
|
|
call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
|
|
|
_dest_place: &mir::Place<'tcx>,
|
|
|
|
) {
|
2017-11-27 08:06:36 +00:00
|
|
|
let move_data = self.move_data();
|
|
|
|
let bits_per_block = self.bits_per_block();
|
|
|
|
let init_loc_map = &move_data.init_loc_map;
|
|
|
|
|
|
|
|
let call_loc = Location {
|
|
|
|
block: call_bb,
|
2019-06-03 18:26:48 -04:00
|
|
|
statement_index: self.body[call_bb].statements.len(),
|
2017-11-27 08:06:36 +00:00
|
|
|
};
|
|
|
|
for init_index in &init_loc_map[call_loc] {
|
|
|
|
assert!(init_index.index() < bits_per_block);
|
Merge indexed_set.rs into bitvec.rs, and rename it bit_set.rs.
Currently we have two files implementing bitsets (and 2D bit matrices).
This commit combines them into one, taking the best features from each.
This involves renaming a lot of things. The high level changes are as
follows.
- bitvec.rs --> bit_set.rs
- indexed_set.rs --> (removed)
- BitArray + IdxSet --> BitSet (merged, see below)
- BitVector --> GrowableBitSet
- {,Sparse,Hybrid}IdxSet --> {,Sparse,Hybrid}BitSet
- BitMatrix --> BitMatrix
- SparseBitMatrix --> SparseBitMatrix
The changes within the bitset types themselves are as follows.
```
OLD OLD NEW
BitArray<C> IdxSet<T> BitSet<T>
-------- ------ ------
grow - grow
new - (remove)
new_empty new_empty new_empty
new_filled new_filled new_filled
- to_hybrid to_hybrid
clear clear clear
set_up_to set_up_to set_up_to
clear_above - clear_above
count - count
contains(T) contains(&T) contains(T)
contains_all - superset
is_empty - is_empty
insert(T) add(&T) insert(T)
insert_all - insert_all()
remove(T) remove(&T) remove(T)
words words words
words_mut words_mut words_mut
- overwrite overwrite
merge union union
- subtract subtract
- intersect intersect
iter iter iter
```
In general, when choosing names I went with:
- names that are more obvious (e.g. `BitSet` over `IdxSet`).
- names that are more like the Rust libraries (e.g. `T` over `C`,
`insert` over `add`);
- names that are more set-like (e.g. `union` over `merge`, `superset`
over `contains_all`, `domain_size` over `num_bits`).
Also, using `T` for index arguments seems more sensible than `&T` --
even though the latter is standard in Rust collection types -- because
indices are always copyable. It also results in fewer `&` and `*`
sigils in practice.
2018-09-14 15:07:25 +10:00
|
|
|
in_out.insert(*init_index);
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> BitSetOperator for MaybeInitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
2018-09-17 13:52:35 +10:00
|
|
|
fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
|
|
|
|
inout_set.union(in_set) // "maybe" means we union effects of both preds
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> BitSetOperator for MaybeUninitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
2018-09-17 13:52:35 +10:00
|
|
|
fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
|
|
|
|
inout_set.union(in_set) // "maybe" means we union effects of both preds
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> BitSetOperator for DefinitelyInitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
2018-09-17 13:52:35 +10:00
|
|
|
fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
|
|
|
|
inout_set.intersect(in_set) // "definitely" means we intersect effects of both preds
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> BitSetOperator for EverInitializedPlaces<'a, 'tcx> {
|
2017-11-27 08:06:36 +00:00
|
|
|
#[inline]
|
2018-09-17 13:52:35 +10:00
|
|
|
fn join<T: Idx>(&self, inout_set: &mut BitSet<T>, in_set: &BitSet<T>) -> bool {
|
|
|
|
inout_set.union(in_set) // inits from both preds are in scope
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-05-24 13:26:54 +02:00
|
|
|
// 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.
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> InitialFlow for MaybeInitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
|
|
|
fn bottom_value() -> bool {
|
|
|
|
false // bottom = uninitialized
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> InitialFlow for MaybeUninitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
|
|
|
fn bottom_value() -> bool {
|
|
|
|
false // bottom = initialized (start_block_effect counters this at outset)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> InitialFlow for DefinitelyInitializedPlaces<'a, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
|
|
|
fn bottom_value() -> bool {
|
|
|
|
true // bottom = initialized (start_block_effect counters this at outset)
|
|
|
|
}
|
|
|
|
}
|
2017-11-08 18:32:08 +03:00
|
|
|
|
2019-06-14 00:48:52 +03:00
|
|
|
impl<'a, 'tcx> InitialFlow for EverInitializedPlaces<'a, 'tcx> {
|
2017-11-27 08:06:36 +00:00
|
|
|
#[inline]
|
|
|
|
fn bottom_value() -> bool {
|
|
|
|
false // bottom = no initialized variables by default
|
|
|
|
}
|
|
|
|
}
|