提交 83ddc333 编写于 作者: B bors

Auto merge of #53314 - nikomatsakis:nll-invert-liveness, r=pnkfelix

NLL: experiment with inverting liveness

I got inspired to see what would happen here.

Fixes #52460

r? @pnkfelix
......@@ -194,12 +194,12 @@ pub fn basic_blocks_and_local_decls_mut(
}
#[inline]
pub fn predecessors(&self) -> ReadGuard<IndexVec<BasicBlock, Vec<BasicBlock>>> {
pub fn predecessors(&self) -> ReadGuard<'_, IndexVec<BasicBlock, Vec<BasicBlock>>> {
self.cache.predecessors(self)
}
#[inline]
pub fn predecessors_for(&self, bb: BasicBlock) -> ReadGuard<Vec<BasicBlock>> {
pub fn predecessors_for(&self, bb: BasicBlock) -> ReadGuard<'_, Vec<BasicBlock>> {
ReadGuard::map(self.predecessors(), |p| &p[bb])
}
......@@ -328,6 +328,14 @@ pub fn is_sub_scope(&self, mut sub: SourceScope, sup: SourceScope) -> bool {
pub fn return_ty(&self) -> Ty<'tcx> {
self.local_decls[RETURN_PLACE].ty
}
/// Get the location of the terminator for the given block
pub fn terminator_loc(&self, bb: BasicBlock) -> Location {
Location {
block: bb,
statement_index: self[bb].statements.len(),
}
}
}
#[derive(Copy, Clone, Debug, RustcEncodable, RustcDecodable)]
......
......@@ -38,13 +38,13 @@ pub fn dominators_given_rpo<G: ControlFlowGraph>(
// compute the post order index (rank) for each node
let mut post_order_rank: IndexVec<G::Node, usize> =
IndexVec::from_elem_n(usize::default(), graph.num_nodes());
(0..graph.num_nodes()).map(|_| 0).collect();
for (index, node) in rpo.iter().rev().cloned().enumerate() {
post_order_rank[node] = index;
}
let mut immediate_dominators: IndexVec<G::Node, Option<G::Node>> =
IndexVec::from_elem_n(Option::default(), graph.num_nodes());
(0..graph.num_nodes()).map(|_| None).collect();
immediate_dominators[start_node] = Some(start_node);
let mut changed = true;
......
......@@ -20,6 +20,8 @@
html_favicon_url = "https://www.rust-lang.org/favicon.ico",
html_root_url = "https://doc.rust-lang.org/nightly/")]
#![feature(in_band_lifetimes)]
#![feature(impl_header_lifetime_elision)]
#![feature(unboxed_closures)]
#![feature(fn_traits)]
#![feature(unsize)]
......@@ -86,6 +88,7 @@
pub mod transitive_relation;
pub mod tuple_slice;
pub use ena::unify;
pub mod vec_linked_list;
pub mod work_queue;
pub mod fingerprint;
......
// Copyright 2014 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.
use indexed_vec::{Idx, IndexVec};
pub fn iter<Ls>(
first: Option<Ls::LinkIndex>,
links: &'a Ls,
) -> impl Iterator<Item = Ls::LinkIndex> + 'a
where
Ls: Links,
{
VecLinkedListIterator {
links: links,
current: first,
}
}
pub struct VecLinkedListIterator<Ls>
where
Ls: Links,
{
links: Ls,
current: Option<Ls::LinkIndex>,
}
impl<Ls> Iterator for VecLinkedListIterator<Ls>
where
Ls: Links,
{
type Item = Ls::LinkIndex;
fn next(&mut self) -> Option<Ls::LinkIndex> {
if let Some(c) = self.current {
self.current = <Ls as Links>::next(&self.links, c);
Some(c)
} else {
None
}
}
}
pub trait Links {
type LinkIndex: Copy;
fn next(links: &Self, index: Self::LinkIndex) -> Option<Self::LinkIndex>;
}
impl<Ls> Links for &Ls
where
Ls: Links,
{
type LinkIndex = Ls::LinkIndex;
fn next(links: &Self, index: Ls::LinkIndex) -> Option<Ls::LinkIndex> {
<Ls as Links>::next(links, index)
}
}
pub trait LinkElem {
type LinkIndex: Copy;
fn next(elem: &Self) -> Option<Self::LinkIndex>;
}
impl<L, E> Links for IndexVec<L, E>
where
E: LinkElem<LinkIndex = L>,
L: Idx,
{
type LinkIndex = L;
fn next(links: &Self, index: L) -> Option<L> {
<E as LinkElem>::next(&links[index])
}
}
......@@ -89,6 +89,10 @@ fn reset_to_entry_of(&mut self, bb: BasicBlock) {
each_flow!(self, reset_to_entry_of(bb));
}
fn reset_to_exit_of(&mut self, bb: BasicBlock) {
each_flow!(self, reset_to_exit_of(bb));
}
fn reconstruct_statement_effect(&mut self, location: Location) {
each_flow!(self, reconstruct_statement_effect(location));
}
......
......@@ -17,7 +17,7 @@
use rustc::mir::{Local, Location, Mir};
use rustc::ty::{RegionVid, TyCtxt};
use rustc_data_structures::fx::FxHashSet;
use util::liveness::{self, DefUse, LivenessMode};
use util::liveness::{self, DefUse};
crate fn find<'tcx>(
mir: &Mir<'tcx>,
......@@ -32,10 +32,6 @@
tcx,
region_vid,
start_point,
liveness_mode: LivenessMode {
include_regular_use: true,
include_drops: true,
},
};
uf.find()
......@@ -47,7 +43,6 @@ struct UseFinder<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_vid: RegionVid,
start_point: Location,
liveness_mode: LivenessMode,
}
impl<'cx, 'gcx, 'tcx> UseFinder<'cx, 'gcx, 'tcx> {
......@@ -108,7 +103,6 @@ fn def_use(&self, location: Location, thing: &dyn MirVisitable<'tcx>) -> Option<
mir: self.mir,
tcx: self.tcx,
region_vid: self.region_vid,
liveness_mode: self.liveness_mode,
def_use_result: None,
};
......@@ -122,7 +116,6 @@ struct DefUseVisitor<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
mir: &'cx Mir<'tcx>,
tcx: TyCtxt<'cx, 'gcx, 'tcx>,
region_vid: RegionVid,
liveness_mode: LivenessMode,
def_use_result: Option<DefUseResult>,
}
......@@ -146,23 +139,12 @@ fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Locati
});
if found_it {
match liveness::categorize(context, self.liveness_mode) {
Some(DefUse::Def) => {
self.def_use_result = Some(DefUseResult::Def);
}
Some(DefUse::Use) => {
self.def_use_result = if context.is_drop() {
Some(DefUseResult::UseDrop { local })
} else {
Some(DefUseResult::UseLive { local })
};
}
None => {
self.def_use_result = None;
}
}
self.def_use_result = match liveness::categorize(context) {
Some(DefUse::Def) => Some(DefUseResult::Def),
Some(DefUse::Use) => Some(DefUseResult::UseLive { local }),
Some(DefUse::Drop) => Some(DefUseResult::UseDrop { local }),
None => None,
};
}
}
}
......@@ -12,7 +12,7 @@
use borrow_check::location::{LocationIndex, LocationTable};
use borrow_check::nll::facts::AllFactsExt;
use borrow_check::nll::type_check::{MirTypeckResults, MirTypeckRegionConstraints};
use borrow_check::nll::type_check::liveness::liveness_map::{NllLivenessMap, LocalWithRegion};
use borrow_check::nll::type_check::liveness::liveness_map::NllLivenessMap;
use borrow_check::nll::region_infer::values::RegionValueElements;
use dataflow::indexes::BorrowIndex;
use dataflow::move_paths::MoveData;
......@@ -22,9 +22,7 @@
use rustc::infer::InferCtxt;
use rustc::mir::{ClosureOutlivesSubject, ClosureRegionRequirements, Mir};
use rustc::ty::{self, RegionKind, RegionVid};
use rustc::util::nodemap::FxHashMap;
use rustc_errors::Diagnostic;
use std::collections::BTreeSet;
use std::fmt::Debug;
use std::env;
use std::io;
......@@ -32,12 +30,11 @@
use std::rc::Rc;
use std::str::FromStr;
use transform::MirSource;
use util::liveness::{LivenessResults, LiveVarSet};
use self::mir_util::PassWhere;
use polonius_engine::{Algorithm, Output};
use util as mir_util;
use util::pretty::{self, ALIGN};
use util::pretty;
mod constraint_generation;
pub mod explain_borrow;
......@@ -111,8 +108,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
let MirTypeckResults {
constraints,
universal_region_relations,
liveness,
liveness_map,
} = type_check::type_check(
infcx,
param_env,
......@@ -205,8 +200,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
// write unit-tests, as well as helping with debugging.
dump_mir_results(
infcx,
&liveness,
&liveness_map,
MirSource::item(def_id),
&mir,
&regioncx,
......@@ -222,8 +215,6 @@ pub(in borrow_check) fn compute_regions<'cx, 'gcx, 'tcx>(
fn dump_mir_results<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
liveness: &LivenessResults<LocalWithRegion>,
liveness_map: &NllLivenessMap,
source: MirSource,
mir: &Mir<'tcx>,
regioncx: &RegionInferenceContext,
......@@ -233,34 +224,6 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
return;
}
let regular_liveness_per_location: FxHashMap<_, _> = mir
.basic_blocks()
.indices()
.flat_map(|bb| {
let mut results = vec![];
liveness
.regular
.simulate_block(&mir, bb, liveness_map, |location, local_set| {
results.push((location, local_set.clone()));
});
results
})
.collect();
let drop_liveness_per_location: FxHashMap<_, _> = mir
.basic_blocks()
.indices()
.flat_map(|bb| {
let mut results = vec![];
liveness
.drop
.simulate_block(&mir, bb, liveness_map, |location, local_set| {
results.push((location, local_set.clone()));
});
results
})
.collect();
mir_util::dump_mir(
infcx.tcx,
None,
......@@ -283,26 +246,10 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
}
}
PassWhere::BeforeLocation(location) => {
let s = live_variable_set(
&regular_liveness_per_location[&location],
&drop_liveness_per_location[&location],
);
writeln!(
out,
"{:ALIGN$} | Live variables on entry to {:?}: {}",
"",
location,
s,
ALIGN = ALIGN
)?;
PassWhere::BeforeLocation(_) => {
}
// After each basic block, dump out the values
// that are live on exit from the basic block.
PassWhere::AfterTerminator(bb) => {
let s = live_variable_set(&liveness.regular.outs[bb], &liveness.drop.outs[bb]);
writeln!(out, " | Live variables on exit from {:?}: {}", bb, s)?;
PassWhere::AfterTerminator(_) => {
}
PassWhere::BeforeBlock(_) | PassWhere::AfterLocation(_) | PassWhere::AfterCFG => {}
......@@ -420,33 +367,3 @@ fn to_region_vid(self) -> RegionVid {
self
}
}
fn live_variable_set(
regular: &LiveVarSet<LocalWithRegion>,
drops: &LiveVarSet<LocalWithRegion>
) -> String {
// sort and deduplicate:
let all_locals: BTreeSet<_> = regular.iter().chain(drops.iter()).collect();
// construct a string with each local, including `(drop)` if it is
// only dropped, versus a regular use.
let mut string = String::new();
for local in all_locals {
string.push_str(&format!("{:?}", local));
if !regular.contains(&local) {
assert!(drops.contains(&local));
string.push_str(" (drop)");
}
string.push_str(", ");
}
let len = if string.is_empty() {
0
} else {
string.len() - 2
};
format!("[{}]", &string[..len])
}
......@@ -10,7 +10,7 @@
use rustc::mir::{BasicBlock, Location, Mir};
use rustc::ty::{self, RegionVid};
use rustc_data_structures::bitvec::SparseBitMatrix;
use rustc_data_structures::bitvec::{BitArray, SparseBitMatrix};
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_vec::IndexVec;
use std::fmt::Debug;
......@@ -20,13 +20,18 @@
crate struct RegionValueElements {
/// For each basic block, how many points are contained within?
statements_before_block: IndexVec<BasicBlock, usize>,
/// Map backward from each point to the basic block that it
/// belongs to.
basic_blocks: IndexVec<PointIndex, BasicBlock>,
num_points: usize,
}
impl RegionValueElements {
crate fn new(mir: &Mir<'_>) -> Self {
let mut num_points = 0;
let statements_before_block = mir
let statements_before_block: IndexVec<BasicBlock, usize> = mir
.basic_blocks()
.iter()
.map(|block_data| {
......@@ -41,14 +46,25 @@ impl RegionValueElements {
);
debug!("RegionValueElements: num_points={:#?}", num_points);
let mut basic_blocks = IndexVec::with_capacity(num_points);
for (bb, bb_data) in mir.basic_blocks().iter_enumerated() {
basic_blocks.extend((0 .. bb_data.statements.len() + 1).map(|_| bb));
}
Self {
statements_before_block,
basic_blocks,
num_points,
}
}
/// Total number of point indices
crate fn num_points(&self) -> usize {
self.num_points
}
/// Converts a `Location` into a `PointIndex`. O(1).
fn point_from_location(&self, location: Location) -> PointIndex {
crate fn point_from_location(&self, location: Location) -> PointIndex {
let Location {
block,
statement_index,
......@@ -57,39 +73,50 @@ fn point_from_location(&self, location: Location) -> PointIndex {
PointIndex::new(start_index + statement_index)
}
/// Converts a `PointIndex` back to a location. O(N) where N is
/// the number of blocks; could be faster if we ever cared.
crate fn to_location(&self, i: PointIndex) -> Location {
let point_index = i.index();
// Find the basic block. We have a vector with the
// starting index of the statement in each block. Imagine
// we have statement #22, and we have a vector like:
//
// [0, 10, 20]
//
// In that case, this represents point_index 2 of
// basic block BB2. We know this because BB0 accounts for
// 0..10, BB1 accounts for 11..20, and BB2 accounts for
// 20...
//
// To compute this, we could do a binary search, but
// because I am lazy we instead iterate through to find
// the last point where the "first index" (0, 10, or 20)
// was less than the statement index (22). In our case, this will
// be (BB2, 20).
//
// Nit: we could do a binary search here but I'm too lazy.
let (block, &first_index) = self
.statements_before_block
.iter_enumerated()
.filter(|(_, first_index)| **first_index <= point_index)
.last()
.unwrap();
Location {
block,
statement_index: point_index - first_index,
/// Converts a `Location` into a `PointIndex`. O(1).
crate fn entry_point(&self, block: BasicBlock) -> PointIndex {
let start_index = self.statements_before_block[block];
PointIndex::new(start_index)
}
/// Converts a `PointIndex` back to a location. O(1).
crate fn to_location(&self, index: PointIndex) -> Location {
assert!(index.index() < self.num_points);
let block = self.basic_blocks[index];
let start_index = self.statements_before_block[block];
let statement_index = index.index() - start_index;
Location { block, statement_index }
}
/// Sometimes we get point-indices back from bitsets that may be
/// out of range (because they round up to the nearest 2^N number
/// of bits). Use this function to filter such points out if you
/// like.
crate fn point_in_range(&self, index: PointIndex) -> bool {
index.index() < self.num_points
}
/// Pushes all predecessors of `index` onto `stack`.
crate fn push_predecessors(
&self,
mir: &Mir<'_>,
index: PointIndex,
stack: &mut Vec<PointIndex>,
) {
let Location { block, statement_index } = self.to_location(index);
if statement_index == 0 {
// If this is a basic block head, then the predecessors are
// the the terminators of other basic blocks
stack.extend(
mir
.predecessors_for(block)
.iter()
.map(|&pred_bb| mir.terminator_loc(pred_bb))
.map(|pred_loc| self.point_from_location(pred_loc)),
);
} else {
// Otherwise, the pred is just the previous statement
stack.push(PointIndex::new(index.index() - 1));
}
}
}
......@@ -151,6 +178,13 @@ impl<N: Idx> LivenessValues<N> {
self.points.add(row, index)
}
/// Adds all the elements in the given bit array into the given
/// region. Returns true if any of them are newly added.
crate fn add_elements(&mut self, row: N, locations: &BitArray<PointIndex>) -> bool {
debug!("LivenessValues::add_elements(row={:?}, locations={:?})", row, locations);
self.points.merge_into(row, locations)
}
/// Adds all the control-flow points to the values for `r`.
crate fn add_all_points(&mut self, row: N) {
self.points.add_all(row);
......@@ -169,6 +203,7 @@ impl<N: Idx> LivenessValues<N> {
.row(r)
.into_iter()
.flat_map(|set| set.iter())
.take_while(|&p| self.elements.point_in_range(p))
.map(|p| self.elements.to_location(p))
.map(RegionElement::Location),
)
......@@ -277,7 +312,11 @@ impl<N: Idx> RegionValues<N> {
self.points
.row(r)
.into_iter()
.flat_map(move |set| set.iter().map(move |p| self.elements.to_location(p)))
.flat_map(move |set| {
set.iter()
.take_while(move |&p| self.elements.point_in_range(p))
.map(move |p| self.elements.to_location(p))
})
}
/// Returns just the universal regions that are contained in a given region's value.
......@@ -366,6 +405,19 @@ fn contained_in_row<N: Idx>(self, values: &RegionValues<N>, row: N) -> bool {
}
}
crate fn location_set_str(
elements: &RegionValueElements,
points: impl IntoIterator<Item = PointIndex>,
) -> String {
region_value_str(
points
.into_iter()
.take_while(|&p| elements.point_in_range(p))
.map(|p| elements.to_location(p))
.map(RegionElement::Location),
)
}
fn region_value_str(elements: impl IntoIterator<Item = RegionElement>) -> String {
let mut result = String::new();
result.push_str("{");
......
......@@ -10,9 +10,9 @@
//! For the NLL computation, we need to compute liveness, but only for those
//! local variables whose types contain regions. The others are not of interest
//! to us. This file defines a new index type (LocalWithRegion) that indexes into
//! to us. This file defines a new index type (LiveVar) that indexes into
//! a list of "variables whose type contain regions". It also defines a map from
//! Local to LocalWithRegion and vice versa -- this map can be given to the
//! Local to LiveVar and vice versa -- this map can be given to the
//! liveness code so that it only operates over variables with regions in their
//! types, instead of all variables.
......@@ -23,7 +23,7 @@
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use util::liveness::LiveVariableMap;
/// Map between Local and LocalWithRegion indices: the purpose of this
/// Map between Local and LiveVar indices: the purpose of this
/// map is to define the subset of local variables for which we need
/// to do a liveness computation. We only need to compute whether a
/// variable `X` is live if that variable contains some region `R` in
......@@ -32,10 +32,10 @@
crate struct NllLivenessMap {
/// For each local variable, contains `Some(i)` if liveness is
/// needed for this variable.
pub from_local: IndexVec<Local, Option<LocalWithRegion>>,
pub from_local: IndexVec<Local, Option<LiveVar>>,
/// For each `LocalWithRegion`, maps back to the original `Local` index.
pub to_local: IndexVec<LocalWithRegion, Local>,
/// For each `LiveVar`, maps back to the original `Local` index.
pub to_local: IndexVec<LiveVar, Local>,
}
impl LiveVariableMap for NllLivenessMap {
......@@ -43,7 +43,7 @@ fn from_local(&self, local: Local) -> Option<Self::LiveVar> {
self.from_local[local]
}
type LiveVar = LocalWithRegion;
type LiveVar = LiveVar;
fn from_live_var(&self, local: Self::LiveVar) -> Local {
self.to_local[local]
......@@ -93,5 +93,10 @@ impl NllLivenessMap {
}
}
/// Index given to each local variable whose type contains a region.
newtype_index!(LocalWithRegion);
/// Index given to each local variable for which we need to
/// compute liveness information. For many locals, we are able to
/// skip liveness information: for example, those variables whose
/// types contain no regions.
newtype_index!(
LiveVar
);
// Copyright 2018 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.
use borrow_check::nll::region_infer::values::{PointIndex, RegionValueElements};
use borrow_check::nll::type_check::liveness::liveness_map::{LiveVar, NllLivenessMap};
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::{Local, Location, Mir};
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::vec_linked_list as vll;
use util::liveness::{categorize, DefUse, LiveVariableMap};
/// A map that cross references each local with the locations where it
/// is defined (assigned), used, or dropped. Used during liveness
/// computation.
crate struct LocalUseMap<'me> {
liveness_map: &'me NllLivenessMap,
/// Head of a linked list of **definitions** of each variable --
/// definition in this context means assignment, e.g. `x` is
/// defined in `x = y` but not `y`; that first def is the head of
/// a linked list that lets you enumerate all places the variable
/// is assigned.
first_def_at: IndexVec<LiveVar, Option<AppearanceIndex>>,
/// Head of a linked list of **uses** of each variable -- use in
/// this context means that the existing value of the variable is
/// read or modified. e.g., `y` is used in `x = y` but not `x`.
/// Note that `DROP(x)` terminators are excluded from this list.
first_use_at: IndexVec<LiveVar, Option<AppearanceIndex>>,
/// Head of a linked list of **drops** of each variable -- these
/// are a special category of uses corresponding to the drop that
/// we add for each local variable.
first_drop_at: IndexVec<LiveVar, Option<AppearanceIndex>>,
appearances: IndexVec<AppearanceIndex, Appearance>,
}
struct Appearance {
point_index: PointIndex,
next: Option<AppearanceIndex>,
}
newtype_index!(AppearanceIndex);
impl vll::LinkElem for Appearance {
type LinkIndex = AppearanceIndex;
fn next(elem: &Self) -> Option<AppearanceIndex> {
elem.next
}
}
impl LocalUseMap<'me> {
crate fn build(
liveness_map: &'me NllLivenessMap,
elements: &RegionValueElements,
mir: &Mir<'_>,
) -> Self {
let nones = IndexVec::from_elem_n(None, liveness_map.num_variables());
let mut local_use_map = LocalUseMap {
liveness_map,
first_def_at: nones.clone(),
first_use_at: nones.clone(),
first_drop_at: nones,
appearances: IndexVec::new(),
};
LocalUseMapBuild {
local_use_map: &mut local_use_map,
elements,
}.visit_mir(mir);
local_use_map
}
crate fn defs(&self, local: LiveVar) -> impl Iterator<Item = PointIndex> + '_ {
vll::iter(self.first_def_at[local], &self.appearances)
.map(move |aa| self.appearances[aa].point_index)
}
crate fn uses(&self, local: LiveVar) -> impl Iterator<Item = PointIndex> + '_ {
vll::iter(self.first_use_at[local], &self.appearances)
.map(move |aa| self.appearances[aa].point_index)
}
crate fn drops(&self, local: LiveVar) -> impl Iterator<Item = PointIndex> + '_ {
vll::iter(self.first_drop_at[local], &self.appearances)
.map(move |aa| self.appearances[aa].point_index)
}
}
struct LocalUseMapBuild<'me, 'map: 'me> {
local_use_map: &'me mut LocalUseMap<'map>,
elements: &'me RegionValueElements,
}
impl LocalUseMapBuild<'_, '_> {
fn insert_def(&mut self, local: LiveVar, location: Location) {
Self::insert(
self.elements,
&mut self.local_use_map.first_def_at[local],
&mut self.local_use_map.appearances,
location,
);
}
fn insert_use(&mut self, local: LiveVar, location: Location) {
Self::insert(
self.elements,
&mut self.local_use_map.first_use_at[local],
&mut self.local_use_map.appearances,
location,
);
}
fn insert_drop(&mut self, local: LiveVar, location: Location) {
Self::insert(
self.elements,
&mut self.local_use_map.first_drop_at[local],
&mut self.local_use_map.appearances,
location,
);
}
fn insert(
elements: &RegionValueElements,
first_appearance: &mut Option<AppearanceIndex>,
appearances: &mut IndexVec<AppearanceIndex, Appearance>,
location: Location,
) {
let point_index = elements.point_from_location(location);
let appearance_index = appearances.push(Appearance {
point_index,
next: *first_appearance,
});
*first_appearance = Some(appearance_index);
}
}
impl Visitor<'tcx> for LocalUseMapBuild<'_, '_> {
fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, location: Location) {
if let Some(local_with_region) = self.local_use_map.liveness_map.from_local(local) {
match categorize(context) {
Some(DefUse::Def) => self.insert_def(local_with_region, location),
Some(DefUse::Use) => self.insert_use(local_with_region, location),
Some(DefUse::Drop) => self.insert_drop(local_with_region, location),
_ => (),
}
}
}
}
......@@ -8,26 +8,23 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
use borrow_check::nll::region_infer::values::RegionValueElements;
use borrow_check::nll::constraints::ConstraintSet;
use borrow_check::nll::type_check::AtLocation;
use borrow_check::nll::{LocalWithRegion, NllLivenessMap};
use borrow_check::nll::NllLivenessMap;
use borrow_check::nll::universal_regions::UniversalRegions;
use dataflow::move_paths::{HasMoveData, MoveData};
use dataflow::move_paths::MoveData;
use dataflow::MaybeInitializedPlaces;
use dataflow::{FlowAtLocation, FlowsAtLocation};
use rustc::infer::canonical::QueryRegionConstraint;
use rustc::mir::{BasicBlock, Location, Mir};
use rustc::traits::query::dropck_outlives::DropckOutlivesResult;
use rustc::traits::query::type_op::outlives::DropckOutlives;
use rustc::traits::query::type_op::TypeOp;
use rustc::ty::{RegionVid, Ty, TypeFoldable};
use rustc_data_structures::fx::{FxHashMap, FxHashSet};
use dataflow::FlowAtLocation;
use rustc::mir::Mir;
use rustc::ty::RegionVid;
use rustc_data_structures::fx::FxHashSet;
use std::rc::Rc;
use util::liveness::{LiveVariableMap, LivenessResults};
use super::TypeChecker;
crate mod liveness_map;
mod local_use_map;
mod trace;
/// Combines liveness analysis with initialization analysis to
/// determine which variables are live at which points, both due to
......@@ -38,40 +35,23 @@
/// NB. This computation requires normalization; therefore, it must be
/// performed before
pub(super) fn generate<'gcx, 'tcx>(
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
typeck: &mut TypeChecker<'_, 'gcx, 'tcx>,
mir: &Mir<'tcx>,
elements: &Rc<RegionValueElements>,
flow_inits: &mut FlowAtLocation<MaybeInitializedPlaces<'_, 'gcx, 'tcx>>,
move_data: &MoveData<'tcx>,
) -> (LivenessResults<LocalWithRegion>, NllLivenessMap) {
) {
debug!("liveness::generate");
let free_regions = {
let borrowck_context = cx.borrowck_context.as_ref().unwrap();
let borrowck_context = typeck.borrowck_context.as_ref().unwrap();
regions_that_outlive_free_regions(
cx.infcx.num_region_vars(),
typeck.infcx.num_region_vars(),
&borrowck_context.universal_regions,
&borrowck_context.constraints.outlives_constraints,
)
};
let liveness_map = NllLivenessMap::compute(cx.tcx(), &free_regions, mir);
let liveness = LivenessResults::compute(mir, &liveness_map);
// For everything else, it is only live where it is actually used.
if !liveness_map.is_empty() {
let mut generator = TypeLivenessGenerator {
cx,
mir,
liveness: &liveness,
flow_inits,
move_data,
drop_data: FxHashMap(),
map: &liveness_map,
};
for bb in mir.basic_blocks().indices() {
generator.add_liveness_constraints(bb);
}
}
(liveness, liveness_map)
let liveness_map = NllLivenessMap::compute(typeck.tcx(), &free_regions, mir);
trace::trace(typeck, mir, elements, flow_inits, move_data, &liveness_map);
}
/// Compute all regions that are (currently) known to outlive free
......@@ -112,198 +92,3 @@ fn regions_that_outlive_free_regions(
// Return the final set of things we visited.
outlives_free_region
}
struct TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx>
where
'typeck: 'gen,
'flow: 'gen,
'tcx: 'typeck + 'flow,
'gcx: 'tcx,
{
cx: &'gen mut TypeChecker<'typeck, 'gcx, 'tcx>,
mir: &'gen Mir<'tcx>,
liveness: &'gen LivenessResults<LocalWithRegion>,
flow_inits: &'gen mut FlowAtLocation<MaybeInitializedPlaces<'flow, 'gcx, 'tcx>>,
move_data: &'gen MoveData<'tcx>,
drop_data: FxHashMap<Ty<'tcx>, DropData<'tcx>>,
map: &'gen NllLivenessMap,
}
struct DropData<'tcx> {
dropck_result: DropckOutlivesResult<'tcx>,
region_constraint_data: Option<Rc<Vec<QueryRegionConstraint<'tcx>>>>,
}
impl<'gen, 'typeck, 'flow, 'gcx, 'tcx> TypeLivenessGenerator<'gen, 'typeck, 'flow, 'gcx, 'tcx> {
/// Liveness constraints:
///
/// > If a variable V is live at point P, then all regions R in the type of V
/// > must include the point P.
fn add_liveness_constraints(&mut self, bb: BasicBlock) {
debug!("add_liveness_constraints(bb={:?})", bb);
self.liveness
.regular
.simulate_block(self.mir, bb, self.map, |location, live_locals| {
for live_local in live_locals.iter() {
let local = self.map.from_live_var(live_local);
let live_local_ty = self.mir.local_decls[local].ty;
Self::push_type_live_constraint(&mut self.cx, live_local_ty, location);
}
});
let mut all_live_locals: Vec<(Location, Vec<LocalWithRegion>)> = vec![];
self.liveness
.drop
.simulate_block(self.mir, bb, self.map, |location, live_locals| {
all_live_locals.push((location, live_locals.iter().collect()));
});
debug!(
"add_liveness_constraints: all_live_locals={:#?}",
all_live_locals
);
let terminator_index = self.mir.basic_blocks()[bb].statements.len();
self.flow_inits.reset_to_entry_of(bb);
while let Some((location, live_locals)) = all_live_locals.pop() {
for live_local in live_locals {
debug!(
"add_liveness_constraints: location={:?} live_local={:?}",
location, live_local
);
if log_enabled!(::log::Level::Debug) {
self.flow_inits.each_state_bit(|mpi_init| {
debug!(
"add_liveness_constraints: location={:?} initialized={:?}",
location,
&self.flow_inits.operator().move_data().move_paths[mpi_init]
);
});
}
let local = self.map.from_live_var(live_local);
let mpi = self.move_data.rev_lookup.find_local(local);
if let Some(initialized_child) = self.flow_inits.has_any_child_of(mpi) {
debug!(
"add_liveness_constraints: mpi={:?} has initialized child {:?}",
self.move_data.move_paths[mpi],
self.move_data.move_paths[initialized_child]
);
let local = self.map.from_live_var(live_local);
let live_local_ty = self.mir.local_decls[local].ty;
self.add_drop_live_constraint(live_local, live_local_ty, location);
}
}
if location.statement_index == terminator_index {
debug!(
"add_liveness_constraints: reconstruct_terminator_effect from {:#?}",
location
);
self.flow_inits.reconstruct_terminator_effect(location);
} else {
debug!(
"add_liveness_constraints: reconstruct_statement_effect from {:#?}",
location
);
self.flow_inits.reconstruct_statement_effect(location);
}
self.flow_inits.apply_local_effect(location);
}
}
/// Some variable with type `live_ty` is "regular live" at
/// `location` -- i.e., it may be used later. This means that all
/// regions appearing in the type `live_ty` must be live at
/// `location`.
fn push_type_live_constraint<T>(
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
value: T,
location: Location,
) where
T: TypeFoldable<'tcx>,
{
debug!(
"push_type_live_constraint(live_ty={:?}, location={:?})",
value, location
);
cx.tcx().for_each_free_region(&value, |live_region| {
if let Some(ref mut borrowck_context) = cx.borrowck_context {
let region_vid = borrowck_context
.universal_regions
.to_region_vid(live_region);
borrowck_context
.constraints
.liveness_constraints
.add_element(region_vid, location);
if let Some(all_facts) = borrowck_context.all_facts {
let start_index = borrowck_context.location_table.start_index(location);
all_facts.region_live_at.push((region_vid, start_index));
let mid_index = borrowck_context.location_table.mid_index(location);
all_facts.region_live_at.push((region_vid, mid_index));
}
}
});
}
/// Some variable with type `live_ty` is "drop live" at `location`
/// -- i.e., it may be dropped later. This means that *some* of
/// the regions in its type must be live at `location`. The
/// precise set will depend on the dropck constraints, and in
/// particular this takes `#[may_dangle]` into account.
fn add_drop_live_constraint(
&mut self,
dropped_local: LocalWithRegion,
dropped_ty: Ty<'tcx>,
location: Location,
) {
debug!(
"add_drop_live_constraint(dropped_local={:?}, dropped_ty={:?}, location={:?})",
dropped_local, dropped_ty, location
);
let drop_data = self.drop_data.entry(dropped_ty).or_insert_with({
let cx = &mut self.cx;
move || Self::compute_drop_data(cx, dropped_ty)
});
if let Some(data) = &drop_data.region_constraint_data {
self.cx.push_region_constraints(location.boring(), data);
}
drop_data.dropck_result.report_overflows(
self.cx.infcx.tcx,
self.mir.source_info(location).span,
dropped_ty,
);
// All things in the `outlives` array may be touched by
// the destructor and must be live at this point.
for &kind in &drop_data.dropck_result.kinds {
Self::push_type_live_constraint(&mut self.cx, kind, location);
}
}
fn compute_drop_data(
cx: &mut TypeChecker<'_, 'gcx, 'tcx>,
dropped_ty: Ty<'tcx>,
) -> DropData<'tcx> {
debug!("compute_drop_data(dropped_ty={:?})", dropped_ty,);
let param_env = cx.param_env;
let (dropck_result, region_constraint_data) = param_env
.and(DropckOutlives::new(dropped_ty))
.fully_perform(cx.infcx)
.unwrap();
DropData {
dropck_result,
region_constraint_data,
}
}
}
......@@ -18,9 +18,7 @@
use borrow_check::nll::region_infer::values::{LivenessValues, RegionValueElements};
use borrow_check::nll::region_infer::{ClosureRegionRequirementsExt, TypeTest};
use borrow_check::nll::type_check::free_region_relations::{CreateResult, UniversalRegionRelations};
use borrow_check::nll::type_check::liveness::liveness_map::NllLivenessMap;
use borrow_check::nll::universal_regions::UniversalRegions;
use borrow_check::nll::LocalWithRegion;
use borrow_check::nll::ToRegionVid;
use dataflow::move_paths::MoveData;
use dataflow::FlowAtLocation;
......@@ -43,7 +41,6 @@
use std::rc::Rc;
use syntax_pos::{Span, DUMMY_SP};
use transform::{MirPass, MirSource};
use util::liveness::LivenessResults;
use rustc_data_structures::fx::FxHashSet;
use rustc_data_structures::indexed_vec::Idx;
......@@ -143,7 +140,7 @@ pub(crate) fn type_check<'gcx, 'tcx>(
all_facts,
);
let (liveness, liveness_map) = {
{
let mut borrowck_context = BorrowCheckContext {
universal_regions,
location_table,
......@@ -169,16 +166,14 @@ pub(crate) fn type_check<'gcx, 'tcx>(
&universal_region_relations,
&normalized_inputs_and_output,
);
liveness::generate(cx, mir, flow_inits, move_data)
liveness::generate(cx, mir, elements, flow_inits, move_data);
},
)
};
);
}
MirTypeckResults {
constraints,
universal_region_relations,
liveness,
liveness_map,
}
}
......@@ -672,8 +667,6 @@ struct BorrowCheckContext<'a, 'tcx: 'a> {
crate struct MirTypeckResults<'tcx> {
crate constraints: MirTypeckRegionConstraints<'tcx>,
crate universal_region_relations: Rc<UniversalRegionRelations<'tcx>>,
crate liveness: LivenessResults<LocalWithRegion>,
crate liveness_map: NllLivenessMap,
}
/// A collection of region constraints that must be satisfied for the
......
......@@ -28,6 +28,15 @@ pub trait FlowsAtLocation {
/// Reset the state bitvector to represent the entry to block `bb`.
fn reset_to_entry_of(&mut self, bb: BasicBlock);
/// Reset the state bitvector to represent the exit of the
/// terminator of block `bb`.
///
/// **Important:** In the case of a `Call` terminator, these
/// effects do *not* include the result of storing the destination
/// of the call, since that is edge-dependent (in other words, the
/// effects don't apply to the unwind edge).
fn reset_to_exit_of(&mut self, bb: BasicBlock);
/// Build gen + kill sets for statement at `loc`.
///
/// Note that invoking this method alone does not change the
......@@ -142,6 +151,12 @@ fn reset_to_entry_of(&mut self, bb: BasicBlock) {
self.curr_state.overwrite(self.base_results.sets().on_entry_set_for(bb.index()));
}
fn reset_to_exit_of(&mut self, bb: BasicBlock) {
self.reset_to_entry_of(bb);
self.curr_state.union(self.base_results.sets().gen_set_for(bb.index()));
self.curr_state.subtract(self.base_results.sets().kill_set_for(bb.index()));
}
fn reconstruct_statement_effect(&mut self, loc: Location) {
self.stmt_gen.clear();
self.stmt_kill.clear();
......
......@@ -454,14 +454,18 @@ pub struct AllSets<E: Idx> {
/// For each block, bits valid on entry to the block.
on_entry_sets: Vec<IdxSet<E>>,
/// 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.)
/// For each block, bits generated by executing the statements +
/// terminator in the block -- with one caveat. In particular, for
/// *call terminators*, the effect of storing the destination is
/// not included, since that only takes effect on the **success**
/// edge (and not the unwind edge).
gen_sets: Vec<HybridIdxSet<E>>,
/// 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.)
/// For each block, bits killed by executing the statements +
/// terminator in the block -- with one caveat. In particular, for
/// *call terminators*, the effect of storing the destination is
/// not included, since that only takes effect on the **success**
/// edge (and not the unwind edge).
kill_sets: Vec<HybridIdxSet<E>>,
}
......
......@@ -17,6 +17,7 @@
#![cfg_attr(not(stage0), feature(nll))]
#![cfg_attr(not(stage0), feature(infer_outlives_requirements))]
#![feature(in_band_lifetimes)]
#![feature(impl_header_lifetime_elision)]
#![feature(slice_patterns)]
#![feature(slice_sort_by_cached_key)]
#![feature(box_patterns)]
......
......@@ -66,7 +66,7 @@
use rustc::ty::{self, TyCtxt, AdtDef, Ty};
use rustc::ty::subst::Substs;
use util::dump_mir;
use util::liveness::{self, IdentityMap, LivenessMode};
use util::liveness::{self, IdentityMap};
use rustc_data_structures::indexed_vec::Idx;
use rustc_data_structures::indexed_set::IdxSet;
use std::collections::HashMap;
......@@ -402,10 +402,6 @@ fn locals_live_across_suspend_points<'a, 'tcx,>(tcx: TyCtxt<'a, 'tcx, 'tcx>,
let mut set = liveness::LiveVarSet::new_empty(mir.local_decls.len());
let mut liveness = liveness::liveness_of_locals(
mir,
LivenessMode {
include_regular_use: true,
include_drops: true,
},
&IdentityMap::new(mir),
);
liveness::dump_mir(
......
......@@ -33,7 +33,6 @@
//! generator yield points, all pre-existing references are invalidated, so this
//! doesn't matter).
use rustc::mir::visit::MirVisitable;
use rustc::mir::visit::{PlaceContext, Visitor};
use rustc::mir::Local;
use rustc::mir::*;
......@@ -50,17 +49,13 @@
pub type LiveVarSet<V> = IdxSet<V>;
/// This gives the result of the liveness analysis at the boundary of
/// basic blocks. You can use `simulate_block` to obtain the
/// intra-block results.
/// basic blocks.
///
/// The `V` type defines the set of variables that we computed
/// liveness for. This is often `Local`, in which case we computed
/// liveness for all variables -- but it can also be some other type,
/// which indicates a subset of the variables within the graph.
pub struct LivenessResult<V: Idx> {
/// Liveness mode in use when these results were computed.
pub mode: LivenessMode,
/// Live variables on exit to each basic block. This is equal to
/// the union of the `ins` for each successor.
pub outs: IndexVec<BasicBlock, LiveVarSet<V>>,
......@@ -104,68 +99,11 @@ fn num_variables(&self) -> usize {
}
}
#[derive(Copy, Clone, Debug)]
pub struct LivenessMode {
/// If true, then we will consider "regular uses" of a variable to be live.
/// For example, if the user writes `foo(x)`, then this is a regular use of
/// the variable `x`.
pub include_regular_use: bool,
/// If true, then we will consider (implicit) drops of a variable
/// to be live. For example, if the user writes `{ let x =
/// vec![...]; .. }`, then the drop at the end of the block is an
/// implicit drop.
///
/// NB. Despite its name, a call like `::std::mem::drop(x)` is
/// **not** considered a drop for this purposes, but rather a
/// regular use.
pub include_drops: bool,
}
/// A combination of liveness results, used in NLL.
pub struct LivenessResults<V: Idx> {
/// Liveness results where a regular use makes a variable X live,
/// but not a drop.
pub regular: LivenessResult<V>,
/// Liveness results where a drop makes a variable X live,
/// but not a regular use.
pub drop: LivenessResult<V>,
}
impl<V: Idx> LivenessResults<V> {
pub fn compute<'tcx>(
mir: &Mir<'tcx>,
map: &impl LiveVariableMap<LiveVar = V>,
) -> LivenessResults<V> {
LivenessResults {
regular: liveness_of_locals(
&mir,
LivenessMode {
include_regular_use: true,
include_drops: false,
},
map,
),
drop: liveness_of_locals(
&mir,
LivenessMode {
include_regular_use: false,
include_drops: true,
},
map,
),
}
}
}
/// Compute which local variables are live within the given function
/// `mir`. The liveness mode `mode` determines what sorts of uses are
/// considered to make a variable live (e.g., do drops count?).
pub fn liveness_of_locals<'tcx, V: Idx>(
mir: &Mir<'tcx>,
mode: LivenessMode,
map: &impl LiveVariableMap<LiveVar = V>,
) -> LivenessResult<V> {
let num_live_vars = map.num_variables();
......@@ -173,7 +111,7 @@ pub fn liveness_of_locals<'tcx, V: Idx>(
let def_use: IndexVec<_, DefsUses<V>> = mir
.basic_blocks()
.iter()
.map(|b| block(mode, map, b, num_live_vars))
.map(|b| block(map, b, num_live_vars))
.collect();
let mut outs: IndexVec<_, LiveVarSet<V>> = mir
......@@ -208,80 +146,17 @@ pub fn liveness_of_locals<'tcx, V: Idx>(
}
}
LivenessResult { mode, outs }
}
impl<V: Idx> LivenessResult<V> {
/// Walks backwards through the statements/terminator in the given
/// basic block `block`. At each point within `block`, invokes
/// the callback `op` with the current location and the set of
/// variables that are live on entry to that location.
pub fn simulate_block<'tcx, OP>(
&self,
mir: &Mir<'tcx>,
block: BasicBlock,
map: &impl LiveVariableMap<LiveVar = V>,
mut callback: OP,
) where
OP: FnMut(Location, &LiveVarSet<V>),
{
let data = &mir[block];
// Get a copy of the bits on exit from the block.
let mut bits = self.outs[block].clone();
// Start with the maximal statement index -- i.e., right before
// the terminator executes.
let mut statement_index = data.statements.len();
// Compute liveness right before terminator and invoke callback.
let terminator_location = Location {
block,
statement_index,
};
let num_live_vars = map.num_variables();
let mut visitor = DefsUsesVisitor {
mode: self.mode,
map,
defs_uses: DefsUses {
defs: LiveVarSet::new_empty(num_live_vars),
uses: LiveVarSet::new_empty(num_live_vars),
},
};
// Visit the various parts of the basic block in reverse. If we go
// forward, the logic in `add_def` and `add_use` would be wrong.
visitor.update_bits_and_do_callback(
terminator_location,
&data.terminator,
&mut bits,
&mut callback,
);
// Compute liveness before each statement (in rev order) and invoke callback.
for statement in data.statements.iter().rev() {
statement_index -= 1;
let statement_location = Location {
block,
statement_index,
};
visitor.defs_uses.clear();
visitor.update_bits_and_do_callback(
statement_location,
statement,
&mut bits,
&mut callback,
);
}
}
LivenessResult { outs }
}
#[derive(Eq, PartialEq, Clone)]
pub enum DefUse {
Def,
Use,
Drop,
}
pub fn categorize<'tcx>(context: PlaceContext<'tcx>, mode: LivenessMode) -> Option<DefUse> {
pub fn categorize<'tcx>(context: PlaceContext<'tcx>) -> Option<DefUse> {
match context {
///////////////////////////////////////////////////////////////////////////
// DEFS
......@@ -322,13 +197,8 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>, mode: LivenessMode) -> Opti
PlaceContext::Inspect |
PlaceContext::Copy |
PlaceContext::Move |
PlaceContext::Validate => {
if mode.include_regular_use {
Some(DefUse::Use)
} else {
None
}
}
PlaceContext::Validate =>
Some(DefUse::Use),
///////////////////////////////////////////////////////////////////////////
// DROP USES
......@@ -338,13 +208,8 @@ pub fn categorize<'tcx>(context: PlaceContext<'tcx>, mode: LivenessMode) -> Opti
// uses in drop are special because `#[may_dangle]`
// attributes can affect whether lifetimes must be live.
PlaceContext::Drop => {
if mode.include_drops {
Some(DefUse::Use)
} else {
None
}
}
PlaceContext::Drop =>
Some(DefUse::Drop),
}
}
......@@ -353,7 +218,6 @@ struct DefsUsesVisitor<'lv, V, M>
V: Idx,
M: LiveVariableMap<LiveVar = V> + 'lv,
{
mode: LivenessMode,
map: &'lv M,
defs_uses: DefsUses<V>,
}
......@@ -365,11 +229,6 @@ struct DefsUses<V: Idx> {
}
impl<V: Idx> DefsUses<V> {
fn clear(&mut self) {
self.uses.clear();
self.defs.clear();
}
fn apply(&self, bits: &mut LiveVarSet<V>) -> bool {
bits.subtract(&self.defs) | bits.union(&self.uses)
}
......@@ -404,29 +263,6 @@ fn add_use(&mut self, index: V) {
}
}
impl<'lv, V, M> DefsUsesVisitor<'lv, V, M>
where
V: Idx,
M: LiveVariableMap<LiveVar = V>,
{
/// Update `bits` with the effects of `value` and call `callback`. We
/// should always visit in reverse order. This method assumes that we have
/// not visited anything before; if you have, clear `bits` first.
fn update_bits_and_do_callback<'tcx, OP>(
&mut self,
location: Location,
value: &impl MirVisitable<'tcx>,
bits: &mut LiveVarSet<V>,
callback: &mut OP,
) where
OP: FnMut(Location, &LiveVarSet<V>),
{
value.apply(location, self);
self.defs_uses.apply(bits);
callback(location, bits);
}
}
impl<'tcx, 'lv, V, M> Visitor<'tcx> for DefsUsesVisitor<'lv, V, M>
where
V: Idx,
......@@ -434,23 +270,21 @@ impl<'tcx, 'lv, V, M> Visitor<'tcx> for DefsUsesVisitor<'lv, V, M>
{
fn visit_local(&mut self, &local: &Local, context: PlaceContext<'tcx>, _: Location) {
if let Some(v_index) = self.map.from_local(local) {
match categorize(context, self.mode) {
match categorize(context) {
Some(DefUse::Def) => self.defs_uses.add_def(v_index),
Some(DefUse::Use) => self.defs_uses.add_use(v_index),
None => (),
Some(DefUse::Use) | Some(DefUse::Drop) => self.defs_uses.add_use(v_index),
_ => (),
}
}
}
}
fn block<'tcx, V: Idx>(
mode: LivenessMode,
map: &impl LiveVariableMap<LiveVar = V>,
b: &BasicBlockData<'tcx>,
locals: usize,
) -> DefsUses<V> {
let mut visitor = DefsUsesVisitor {
mode,
map,
defs_uses: DefsUses {
defs: LiveVarSet::new_empty(locals),
......@@ -526,7 +360,8 @@ pub fn write_mir_fn<'a, 'tcx, V: Idx>(
write_mir_intro(tcx, src, mir, w)?;
for block in mir.basic_blocks().indices() {
let print = |w: &mut dyn Write, prefix, result: &IndexVec<BasicBlock, LiveVarSet<V>>| {
let live: Vec<String> = result[block].iter()
let live: Vec<String> = result[block]
.iter()
.map(|v| map.from_live_var(v))
.map(|local| format!("{:?}", local))
.collect();
......
......@@ -34,15 +34,15 @@ fn main() {
// | '_#4r | Local | ['_#4r]
// |
// | Inferred Region Values
// | '_#0r | U0 | {bb0[0..=127], '_#0r}
// | '_#1r | U0 | {bb0[0..=127], '_#1r}
// | '_#2r | U0 | {bb0[0..=127], '_#2r}
// | '_#3r | U0 | {bb0[0..=127], '_#3r}
// | '_#4r | U0 | {bb0[0..=127], '_#4r}
// | '_#5r | U0 | {bb0[0..=127], '_#1r}
// | '_#6r | U0 | {bb0[0..=127], '_#2r}
// | '_#7r | U0 | {bb0[0..=127], '_#1r}
// | '_#8r | U0 | {bb0[0..=127], '_#3r}
// | '_#0r | U0 | {bb0[0..=1], '_#0r}
// | '_#1r | U0 | {bb0[0..=1], '_#1r}
// | '_#2r | U0 | {bb0[0..=1], '_#2r}
// | '_#3r | U0 | {bb0[0..=1], '_#3r}
// | '_#4r | U0 | {bb0[0..=1], '_#4r}
// | '_#5r | U0 | {bb0[0..=1], '_#1r}
// | '_#6r | U0 | {bb0[0..=1], '_#2r}
// | '_#7r | U0 | {bb0[0..=1], '_#1r}
// | '_#8r | U0 | {bb0[0..=1], '_#3r}
// |
// ...
// fn use_x(_1: &'_#5r mut i32, _2: &'_#6r u32, _3: &'_#7r u32, _4: &'_#8r u32) -> bool {
......
......@@ -8,10 +8,8 @@
// option. This file may not be copied, modified, or distributed
// except according to those terms.
//compile-flags: -Z emit-end-regions -Zborrowck=mir
#![allow(warnings)]
#![feature(nll)]
struct Wrap<'p> { p: &'p mut i32 }
......
error[E0506]: cannot assign to `x` because it is borrowed
--> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:32:5
--> $DIR/maybe-initialized-drop-implicit-fragment-drop.rs:30:5
|
LL | let wrap = Wrap { p: &mut x };
| ------ borrow of `x` occurs here
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册