2016-05-24 13:26:54 +02:00
|
|
|
// 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 <LICENSE-APACHE or
|
|
|
|
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
|
|
|
|
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
|
|
|
|
// option. This file may not be copied, modified, or distributed
|
|
|
|
// except according to those terms.
|
|
|
|
|
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;
|
2016-09-19 23:50:00 +03:00
|
|
|
use rustc::mir::{self, Mir, Location};
|
2016-10-05 22:43:27 -04:00
|
|
|
use rustc_data_structures::bitslice::{BitwiseOperator};
|
|
|
|
use rustc_data_structures::indexed_set::{IdxSet};
|
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;
|
|
|
|
use util::elaborate_drops::DropFlagState;
|
2016-05-24 13:26:54 +02:00
|
|
|
|
2017-11-27 08:06:36 +00:00
|
|
|
use super::move_paths::{HasMoveData, MoveData, MoveOutIndex, MovePathIndex, InitIndex};
|
|
|
|
use super::move_paths::{LookupResult, InitKind};
|
2016-05-24 13:26:54 +02:00
|
|
|
use super::{BitDenotation, BlockSets, DataflowOperator};
|
|
|
|
|
2017-06-26 14:57:26 +02:00
|
|
|
use super::drop_flag_effects_for_function_entry;
|
|
|
|
use super::drop_flag_effects_for_location;
|
2017-11-27 08:06:36 +00:00
|
|
|
use super::{on_lookup_result_bits, for_location_inits};
|
2016-05-24 13:26:54 +02:00
|
|
|
|
2017-09-10 22:34:56 +02:00
|
|
|
mod storage_liveness;
|
|
|
|
|
|
|
|
pub use self::storage_liveness::*;
|
|
|
|
|
2017-07-03 18:40:20 +02:00
|
|
|
#[allow(dead_code)]
|
|
|
|
pub(super) mod borrows;
|
|
|
|
|
2016-05-24 13:26:54 +02:00
|
|
|
/// `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.
|
2017-10-30 05:50:39 -04:00
|
|
|
pub struct MaybeInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
|
|
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
mir: &'a Mir<'tcx>,
|
2017-10-30 05:50:39 -04:00
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx: 'tcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
|
|
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2016-12-26 09:40:15 -05:00
|
|
|
mir: &'a Mir<'tcx>,
|
2017-10-30 05:50:39 -04:00
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
2016-12-26 09:40:15 -05:00
|
|
|
-> Self
|
|
|
|
{
|
|
|
|
MaybeInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
2016-12-26 09:40:15 -05:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
2016-05-24 13:26:54 +02:00
|
|
|
/// `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.
|
2017-10-30 05:50:39 -04:00
|
|
|
pub struct MaybeUninitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
|
|
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
mir: &'a Mir<'tcx>,
|
2017-10-30 05:50:39 -04:00
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
|
|
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2016-12-26 09:40:15 -05:00
|
|
|
mir: &'a Mir<'tcx>,
|
2017-10-30 05:50:39 -04:00
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
2016-12-26 09:40:15 -05:00
|
|
|
-> Self
|
|
|
|
{
|
|
|
|
MaybeUninitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
2016-12-26 09:40:15 -05:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
2016-05-24 13:26:54 +02:00
|
|
|
/// `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.
|
2017-10-30 05:50:39 -04:00
|
|
|
pub struct DefinitelyInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
|
|
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
mir: &'a Mir<'tcx>,
|
2017-10-30 05:50:39 -04:00
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx: 'a> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
|
|
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
2016-12-26 09:40:15 -05:00
|
|
|
mir: &'a Mir<'tcx>,
|
2017-10-30 05:50:39 -04:00
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
2016-12-26 09:40:15 -05:00
|
|
|
-> Self
|
|
|
|
{
|
|
|
|
DefinitelyInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
|
2016-05-24 15:01:48 +02:00
|
|
|
}
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx: 'a> HasMoveData<'tcx> for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
2016-12-26 09:40:15 -05:00
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
2017-11-08 18:32:08 +03:00
|
|
|
/// `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 message may need to report.
|
|
|
|
#[allow(dead_code)]
|
|
|
|
pub struct MovingOutStatements<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
|
|
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
|
|
|
mir: &'a Mir<'tcx>,
|
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'gcx: 'tcx, 'tcx: 'a> MovingOutStatements<'a, 'gcx, 'tcx> {
|
|
|
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
|
|
|
mir: &'a Mir<'tcx>,
|
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
|
|
|
-> Self
|
|
|
|
{
|
|
|
|
MovingOutStatements { tcx: tcx, mir: mir, mdpe: mdpe }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
2017-11-27 08:06:36 +00:00
|
|
|
/// `EverInitializedLvals` tracks all l-values that might have ever been
|
|
|
|
/// 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 }
|
|
|
|
/// }
|
|
|
|
/// ```
|
|
|
|
pub struct EverInitializedLvals<'a, 'gcx: 'tcx, 'tcx: 'a> {
|
|
|
|
tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
|
|
|
mir: &'a Mir<'tcx>,
|
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'gcx: 'tcx, 'tcx: 'a> EverInitializedLvals<'a, 'gcx, 'tcx> {
|
|
|
|
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
|
|
|
|
mir: &'a Mir<'tcx>,
|
|
|
|
mdpe: &'a MoveDataParamEnv<'gcx, 'tcx>)
|
|
|
|
-> Self
|
|
|
|
{
|
|
|
|
EverInitializedLvals { tcx: tcx, mir: mir, mdpe: mdpe }
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
impl<'a, 'gcx, 'tcx> HasMoveData<'tcx> for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
|
|
|
fn move_data(&self) -> &MoveData<'tcx> { &self.mdpe.move_data }
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
|
|
|
state: DropFlagState)
|
|
|
|
{
|
|
|
|
match state {
|
|
|
|
DropFlagState::Absent => sets.kill(&path),
|
|
|
|
DropFlagState::Present => sets.gen(&path),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
|
|
|
state: DropFlagState)
|
|
|
|
{
|
|
|
|
match state {
|
|
|
|
DropFlagState::Absent => sets.gen(&path),
|
|
|
|
DropFlagState::Present => sets.kill(&path),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
fn update_bits(sets: &mut BlockSets<MovePathIndex>, path: MovePathIndex,
|
|
|
|
state: DropFlagState)
|
|
|
|
{
|
|
|
|
match state {
|
|
|
|
DropFlagState::Absent => sets.kill(&path),
|
|
|
|
DropFlagState::Present => sets.gen(&path),
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeInitializedLvals<'a, 'gcx, '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
|
|
|
|
2016-12-26 09:40:15 -05:00
|
|
|
fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>)
|
2016-05-24 13:26:54 +02:00
|
|
|
{
|
|
|
|
drop_flag_effects_for_function_entry(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, self.mdpe,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| {
|
|
|
|
assert!(s == DropFlagState::Present);
|
|
|
|
sets.on_entry.add(&path);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn statement_effect(&self,
|
|
|
|
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(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, 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,
|
|
|
|
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(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, 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 propagate_call_return(&self,
|
|
|
|
in_out: &mut IdxSet<MovePathIndex>,
|
2016-09-19 23:50:00 +03:00
|
|
|
_call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
2017-12-01 14:39:51 +02:00
|
|
|
dest_place: &mir::Place) {
|
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).
|
2016-12-26 09:40:15 -05:00
|
|
|
on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
|
2017-12-01 14:39:51 +02:00
|
|
|
self.move_data().rev_lookup.find(dest_place),
|
2016-06-11 23:47:28 +03:00
|
|
|
|mpi| { in_out.add(&mpi); });
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> BitDenotation for MaybeUninitializedLvals<'a, 'gcx, '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
|
2016-12-26 09:40:15 -05:00
|
|
|
fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>) {
|
2016-05-24 13:26:54 +02:00
|
|
|
// 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(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, self.mdpe,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| {
|
|
|
|
assert!(s == DropFlagState::Present);
|
|
|
|
sets.on_entry.remove(&path);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn statement_effect(&self,
|
|
|
|
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(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, 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,
|
|
|
|
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(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, 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 propagate_call_return(&self,
|
|
|
|
in_out: &mut IdxSet<MovePathIndex>,
|
2016-09-19 23:50:00 +03:00
|
|
|
_call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
2017-12-01 14:39:51 +02:00
|
|
|
dest_place: &mir::Place) {
|
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).
|
2016-12-26 09:40:15 -05:00
|
|
|
on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
|
2017-12-01 14:39:51 +02:00
|
|
|
self.move_data().rev_lookup.find(dest_place),
|
2016-06-11 23:47:28 +03:00
|
|
|
|mpi| { in_out.remove(&mpi); });
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> BitDenotation for DefinitelyInitializedLvals<'a, 'gcx, '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
|
2016-12-26 09:40:15 -05:00
|
|
|
fn start_block_effect(&self, sets: &mut BlockSets<MovePathIndex>) {
|
2016-05-24 13:26:54 +02:00
|
|
|
for e in sets.on_entry.words_mut() { *e = 0; }
|
|
|
|
|
|
|
|
drop_flag_effects_for_function_entry(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, self.mdpe,
|
2016-05-24 13:26:54 +02:00
|
|
|
|path, s| {
|
|
|
|
assert!(s == DropFlagState::Present);
|
|
|
|
sets.on_entry.add(&path);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
fn statement_effect(&self,
|
|
|
|
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(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, 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,
|
|
|
|
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(
|
2016-12-26 09:40:15 -05:00
|
|
|
self.tcx, self.mir, 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 propagate_call_return(&self,
|
|
|
|
in_out: &mut IdxSet<MovePathIndex>,
|
2016-09-19 23:50:00 +03:00
|
|
|
_call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
2017-12-01 14:39:51 +02:00
|
|
|
dest_place: &mir::Place) {
|
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).
|
2016-12-26 09:40:15 -05:00
|
|
|
on_lookup_result_bits(self.tcx, self.mir, self.move_data(),
|
2017-12-01 14:39:51 +02:00
|
|
|
self.move_data().rev_lookup.find(dest_place),
|
2016-06-11 23:47:28 +03:00
|
|
|
|mpi| { in_out.add(&mpi); });
|
2016-05-24 13:26:54 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-08 18:32:08 +03:00
|
|
|
impl<'a, 'gcx, 'tcx> BitDenotation for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|
|
|
type Idx = MoveOutIndex;
|
|
|
|
fn name() -> &'static str { "moving_out" }
|
|
|
|
fn bits_per_block(&self) -> usize {
|
|
|
|
self.move_data().moves.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn start_block_effect(&self, _sets: &mut BlockSets<MoveOutIndex>) {
|
|
|
|
// no move-statements have been executed prior to function
|
|
|
|
// execution, so this method has no effect on `_sets`.
|
|
|
|
}
|
|
|
|
fn statement_effect(&self,
|
|
|
|
sets: &mut BlockSets<MoveOutIndex>,
|
|
|
|
location: Location) {
|
|
|
|
let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
|
|
|
|
let stmt = &mir[location.block].statements[location.statement_index];
|
|
|
|
let loc_map = &move_data.loc_map;
|
|
|
|
let path_map = &move_data.path_map;
|
|
|
|
|
2017-11-10 14:11:25 +03:00
|
|
|
match stmt.kind {
|
2017-11-13 15:46:22 +03:00
|
|
|
// this analysis only tries to find moves explicitly
|
|
|
|
// written by the user, so we ignore the move-outs
|
|
|
|
// created by `StorageDead` and at the beginning
|
|
|
|
// of a function.
|
2017-11-10 14:11:25 +03:00
|
|
|
mir::StatementKind::StorageDead(_) => {}
|
|
|
|
_ => {
|
|
|
|
debug!("stmt {:?} at loc {:?} moves out of move_indexes {:?}",
|
|
|
|
stmt, location, &loc_map[location]);
|
2017-12-04 01:00:46 +02:00
|
|
|
// Every path deinitialized by a *particular move*
|
|
|
|
// has corresponding bit, "gen'ed" (i.e. set)
|
|
|
|
// here, in dataflow vector
|
|
|
|
sets.gen_all_and_assert_dead(&loc_map[location]);
|
2017-11-10 14:11:25 +03:00
|
|
|
}
|
2017-11-08 18:32:08 +03:00
|
|
|
}
|
2017-11-10 14:11:25 +03:00
|
|
|
|
2017-11-27 08:06:36 +00:00
|
|
|
for_location_inits(tcx, mir, move_data, location,
|
2017-12-04 01:00:46 +02:00
|
|
|
|mpi| sets.kill_all(&path_map[mpi]));
|
2017-11-08 18:32:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn terminator_effect(&self,
|
|
|
|
sets: &mut BlockSets<MoveOutIndex>,
|
|
|
|
location: Location)
|
|
|
|
{
|
2017-11-27 08:06:36 +00:00
|
|
|
let (tcx, mir, move_data) = (self.tcx, self.mir, self.move_data());
|
2017-11-08 18:32:08 +03:00
|
|
|
let term = mir[location.block].terminator();
|
|
|
|
let loc_map = &move_data.loc_map;
|
2017-11-27 08:06:36 +00:00
|
|
|
let path_map = &move_data.path_map;
|
|
|
|
|
2017-11-08 18:32:08 +03:00
|
|
|
debug!("terminator {:?} at loc {:?} moves out of move_indexes {:?}",
|
|
|
|
term, location, &loc_map[location]);
|
2017-12-04 01:00:46 +02:00
|
|
|
sets.gen_all_and_assert_dead(&loc_map[location]);
|
2017-11-27 08:06:36 +00:00
|
|
|
|
|
|
|
for_location_inits(tcx, mir, move_data, location,
|
2017-12-04 01:00:46 +02:00
|
|
|
|mpi| sets.kill_all(&path_map[mpi]));
|
2017-11-08 18:32:08 +03:00
|
|
|
}
|
|
|
|
|
|
|
|
fn propagate_call_return(&self,
|
|
|
|
in_out: &mut IdxSet<MoveOutIndex>,
|
|
|
|
_call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
2017-12-01 14:39:51 +02:00
|
|
|
dest_place: &mir::Place) {
|
2017-11-08 18:32:08 +03:00
|
|
|
let move_data = self.move_data();
|
|
|
|
let bits_per_block = self.bits_per_block();
|
|
|
|
|
|
|
|
let path_map = &move_data.path_map;
|
|
|
|
on_lookup_result_bits(self.tcx,
|
|
|
|
self.mir,
|
|
|
|
move_data,
|
2017-12-01 14:39:51 +02:00
|
|
|
move_data.rev_lookup.find(dest_place),
|
2017-11-08 18:32:08 +03:00
|
|
|
|mpi| for moi in &path_map[mpi] {
|
|
|
|
assert!(moi.index() < bits_per_block);
|
|
|
|
in_out.remove(&moi);
|
|
|
|
});
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-27 08:06:36 +00:00
|
|
|
impl<'a, 'gcx, 'tcx> BitDenotation for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
|
|
|
type Idx = InitIndex;
|
|
|
|
fn name() -> &'static str { "ever_init" }
|
|
|
|
fn bits_per_block(&self) -> usize {
|
|
|
|
self.move_data().inits.len()
|
|
|
|
}
|
|
|
|
|
|
|
|
fn start_block_effect(&self, sets: &mut BlockSets<InitIndex>) {
|
2017-12-04 01:00:46 +02:00
|
|
|
sets.gen_all((0..self.mir.arg_count).map(InitIndex::new));
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
|
|
|
fn statement_effect(&self,
|
|
|
|
sets: &mut BlockSets<InitIndex>,
|
|
|
|
location: Location) {
|
|
|
|
let (_, mir, move_data) = (self.tcx, self.mir, self.move_data());
|
|
|
|
let stmt = &mir[location.block].statements[location.statement_index];
|
|
|
|
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 {
|
|
|
|
mir::StatementKind::StorageDead(local) => {
|
|
|
|
// End inits for StorageDead, so that an immutable variable can
|
|
|
|
// be reinitialized on the next iteration of the loop.
|
2017-12-01 14:31:47 +02:00
|
|
|
if let LookupResult::Exact(mpi) = rev_lookup.find(&mir::Place::Local(local)) {
|
2017-11-27 08:06:36 +00:00
|
|
|
debug!("stmt {:?} at loc {:?} clears the ever initialized status of {:?}",
|
2017-12-04 01:00:46 +02:00
|
|
|
stmt, location, &init_path_map[mpi]);
|
|
|
|
sets.kill_all(&init_path_map[mpi]);
|
2017-11-27 08:06:36 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
_ => {}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
fn terminator_effect(&self,
|
|
|
|
sets: &mut BlockSets<InitIndex>,
|
|
|
|
location: Location)
|
|
|
|
{
|
|
|
|
let (mir, move_data) = (self.mir, self.move_data());
|
|
|
|
let term = mir[location.block].terminator();
|
|
|
|
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
|
|
|
}
|
|
|
|
|
|
|
|
fn propagate_call_return(&self,
|
|
|
|
in_out: &mut IdxSet<InitIndex>,
|
|
|
|
call_bb: mir::BasicBlock,
|
|
|
|
_dest_bb: mir::BasicBlock,
|
2017-12-01 14:39:51 +02:00
|
|
|
_dest_place: &mir::Place) {
|
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,
|
|
|
|
statement_index: self.mir[call_bb].statements.len(),
|
|
|
|
};
|
|
|
|
for init_index in &init_loc_map[call_loc] {
|
|
|
|
assert!(init_index.index() < bits_per_block);
|
|
|
|
in_out.add(init_index);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
|
|
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
|
|
|
pred1 | pred2 // "maybe" means we union effects of both preds
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
|
|
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
|
|
|
pred1 | pred2 // "maybe" means we union effects of both preds
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for DefinitelyInitializedLvals<'a, 'gcx, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
|
|
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
|
|
|
pred1 & pred2 // "definitely" means we intersect effects of both preds
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-08 18:32:08 +03:00
|
|
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|
|
|
#[inline]
|
|
|
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
|
|
|
pred1 | pred2 // moves from both preds are in scope
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-11-27 08:06:36 +00:00
|
|
|
impl<'a, 'gcx, 'tcx> BitwiseOperator for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
|
|
|
#[inline]
|
|
|
|
fn join(&self, pred1: usize, pred2: usize) -> usize {
|
|
|
|
pred1 | pred2 // inits from both preds are in scope
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
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.
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeInitializedLvals<'a, 'gcx, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
|
|
|
fn bottom_value() -> bool {
|
|
|
|
false // bottom = uninitialized
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> DataflowOperator for MaybeUninitializedLvals<'a, 'gcx, 'tcx> {
|
2016-05-24 13:26:54 +02:00
|
|
|
#[inline]
|
|
|
|
fn bottom_value() -> bool {
|
|
|
|
false // bottom = initialized (start_block_effect counters this at outset)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2017-10-30 05:50:39 -04:00
|
|
|
impl<'a, 'gcx, 'tcx> DataflowOperator for DefinitelyInitializedLvals<'a, 'gcx, '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
|
|
|
|
|
|
|
impl<'a, 'gcx, 'tcx> DataflowOperator for MovingOutStatements<'a, 'gcx, 'tcx> {
|
|
|
|
#[inline]
|
|
|
|
fn bottom_value() -> bool {
|
|
|
|
false // bottom = no loans in scope by default
|
|
|
|
}
|
|
|
|
}
|
2017-11-27 08:06:36 +00:00
|
|
|
|
|
|
|
impl<'a, 'gcx, 'tcx> DataflowOperator for EverInitializedLvals<'a, 'gcx, 'tcx> {
|
|
|
|
#[inline]
|
|
|
|
fn bottom_value() -> bool {
|
|
|
|
false // bottom = no initialized variables by default
|
|
|
|
}
|
|
|
|
}
|