提交 cafbd99c 编写于 作者: N Niko Matsakis

extend NLL regions to include free region indices and add outlives

上级 7b30e8d6
...@@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> { ...@@ -38,7 +38,7 @@ pub struct Borrows<'a, 'gcx: 'tcx, 'tcx: 'a> {
location_map: FxHashMap<Location, BorrowIndex>, location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>, region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
region_span_map: FxHashMap<RegionKind, Span>, region_span_map: FxHashMap<RegionKind, Span>,
nonlexical_regioncx: Option<&'a RegionInferenceContext>, nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>,
} }
// temporarily allow some dead fields: `kind` and `region` will be // temporarily allow some dead fields: `kind` and `region` will be
...@@ -69,7 +69,7 @@ fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result { ...@@ -69,7 +69,7 @@ fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>, pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
nonlexical_regioncx: Option<&'a RegionInferenceContext>) nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>)
-> Self { -> Self {
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(), let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
location_map: FxHashMap(), location_map: FxHashMap(),
...@@ -140,7 +140,7 @@ fn kill_loans_out_of_scope_at_location(&self, ...@@ -140,7 +140,7 @@ fn kill_loans_out_of_scope_at_location(&self,
if let Some(regioncx) = self.nonlexical_regioncx { if let Some(regioncx) = self.nonlexical_regioncx {
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() { for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
let borrow_region = regioncx.region_value(borrow_data.region.to_region_index()); let borrow_region = regioncx.region_value(borrow_data.region.to_region_index());
if !borrow_region.may_contain(location) && location != borrow_data.location { if !borrow_region.may_contain_point(location) && location != borrow_data.location {
debug!("kill_loans_out_of_scope_at_location: kill{:?} \ debug!("kill_loans_out_of_scope_at_location: kill{:?} \
location={:?} borrow_data={:?}", borrow_index, location, borrow_data); location={:?} borrow_data={:?}", borrow_index, location, borrow_data);
sets.kill(&borrow_index); sets.kill(&borrow_index);
......
...@@ -29,7 +29,7 @@ ...@@ -29,7 +29,7 @@
pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>, infcx: &InferCtxt<'a, 'gcx, 'tcx>,
regioncx: &mut RegionInferenceContext, regioncx: &mut RegionInferenceContext<'tcx>,
mir: &Mir<'tcx>, mir: &Mir<'tcx>,
mir_source: MirSource, mir_source: MirSource,
liveness: &LivenessResults, liveness: &LivenessResults,
...@@ -45,7 +45,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>( ...@@ -45,7 +45,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> { struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>, infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &'cx mut RegionInferenceContext, regioncx: &'cx mut RegionInferenceContext<'tcx>,
mir: &'cx Mir<'tcx>, mir: &'cx Mir<'tcx>,
liveness: &'cx LivenessResults, liveness: &'cx LivenessResults,
mir_source: MirSource, mir_source: MirSource,
......
// Copyright 2017 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.
//! Code to extract the free regions declared on a function and the
//! relationships between them. For example:
//!
//! ```
//! fn foo<'a, 'b, 'c: 'b>() { }
//! ```
//!
//! here we would be returning a map assigning each of `{'a, 'b, 'c}`
//! to an index, as well as the `FreeRegionMap` which can compute
//! relationships between them.
//!
//! The code in this file doesn't *do anything* with those results; it
//! just returns them for other code to use.
use rustc::infer::InferCtxt;
use rustc::middle::free_region::FreeRegionMap;
use rustc::mir::transform::MirSource;
use rustc::ty;
use rustc::ty::subst::Substs;
use rustc::util::nodemap::FxHashMap;
#[derive(Debug)]
pub struct FreeRegions<'tcx> {
/// Given a free region defined on this function (either early- or
/// late-bound), this maps it to its internal region index. The
/// corresponding variable will be "capped" so that it cannot
/// grow.
pub indices: FxHashMap<ty::Region<'tcx>, usize>,
/// The map from the typeck tables telling us how to relate free regions.
pub free_region_map: &'tcx FreeRegionMap<'tcx>,
}
pub fn free_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
source: MirSource,
) -> FreeRegions<'tcx> {
debug!("free_regions(source={:?})", source);
let item_id = source.item_id();
let item_def_id = infcx.tcx.hir.local_def_id(item_id);
let mut indices = FxHashMap();
// Extract the early regions.
let item_substs = Substs::identity_for_item(infcx.tcx, item_def_id);
for item_subst in item_substs {
if let Some(region) = item_subst.as_region() {
insert_free_region(&mut indices, region);
}
}
// Extract the late-bound regions. Use the liberated fn sigs,
// where the late-bound regions will have been converted into free
// regions, and add them to the map.
let fn_hir_id = infcx.tcx.hir.node_to_hir_id(item_id);
let tables = infcx.tcx.typeck_tables_of(item_def_id);
let fn_sig = tables.liberated_fn_sigs()[fn_hir_id].clone();
infcx
.tcx
.for_each_free_region(&fn_sig.inputs_and_output, |region| {
if let ty::ReFree(_) = *region {
insert_free_region(&mut indices, region);
}
});
debug!("free_regions: indices={:#?}", indices);
FreeRegions { indices, free_region_map: &tables.free_region_map }
}
fn insert_free_region<'tcx>(
free_regions: &mut FxHashMap<ty::Region<'tcx>, usize>,
region: ty::Region<'tcx>,
) {
let len = free_regions.len();
free_regions.entry(region).or_insert(len);
}
...@@ -22,6 +22,7 @@ ...@@ -22,6 +22,7 @@
use self::mir_util::PassWhere; use self::mir_util::PassWhere;
mod constraint_generation; mod constraint_generation;
mod free_regions;
mod subtype; mod subtype;
pub(crate) mod region_infer; pub(crate) mod region_infer;
...@@ -36,9 +37,12 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( ...@@ -36,9 +37,12 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>, infcx: &InferCtxt<'a, 'gcx, 'tcx>,
source: MirSource, source: MirSource,
mir: &mut Mir<'tcx>, mir: &mut Mir<'tcx>,
) -> RegionInferenceContext { ) -> RegionInferenceContext<'tcx> {
// Compute named region information.
let free_regions = &free_regions::free_regions(infcx, source);
// Replace all regions with fresh inference variables. // Replace all regions with fresh inference variables.
let num_region_variables = renumber::renumber_mir(infcx, mir); let num_region_variables = renumber::renumber_mir(infcx, free_regions, mir);
// Compute what is live where. // Compute what is live where.
let liveness = &LivenessResults { let liveness = &LivenessResults {
...@@ -61,11 +65,9 @@ pub fn compute_regions<'a, 'gcx, 'tcx>( ...@@ -61,11 +65,9 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
// Create the region inference context, generate the constraints, // Create the region inference context, generate the constraints,
// and then solve them. // and then solve them.
let mut regioncx = RegionInferenceContext::new(num_region_variables); let mut regioncx = RegionInferenceContext::new(free_regions, num_region_variables, mir);
constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness); constraint_generation::generate_constraints(infcx, &mut regioncx, &mir, source, liveness);
let errors = regioncx.solve(infcx, &mir); regioncx.solve(infcx, &mir);
assert!(errors.is_empty(), "FIXME: report region inference failures");
// Dump MIR results into a file, if that is enabled. This let us // Dump MIR results into a file, if that is enabled. This let us
// write unit-tests. // write unit-tests.
...@@ -152,11 +154,15 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( ...@@ -152,11 +154,15 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
#[derive(Clone, Default, PartialEq, Eq)] #[derive(Clone, Default, PartialEq, Eq)]
pub struct Region { pub struct Region {
points: BTreeSet<Location>, points: BTreeSet<Location>,
free_regions: BTreeSet<RegionIndex>,
} }
impl fmt::Debug for Region { impl fmt::Debug for Region {
fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> { fn fmt(&self, formatter: &mut fmt::Formatter) -> Result<(), fmt::Error> {
write!(formatter, "{:?}", self.points) formatter.debug_set()
.entries(&self.points)
.entries(&self.free_regions)
.finish()
} }
} }
...@@ -165,9 +171,17 @@ pub fn add_point(&mut self, point: Location) -> bool { ...@@ -165,9 +171,17 @@ pub fn add_point(&mut self, point: Location) -> bool {
self.points.insert(point) self.points.insert(point)
} }
pub fn may_contain(&self, point: Location) -> bool { pub fn add_free_region(&mut self, region: RegionIndex) -> bool {
self.free_regions.insert(region)
}
pub fn may_contain_point(&self, point: Location) -> bool {
self.points.contains(&point) self.points.contains(&point)
} }
pub fn may_contain_free_region(&self, region: RegionIndex) -> bool {
self.free_regions.contains(&region)
}
} }
newtype_index!(RegionIndex { newtype_index!(RegionIndex {
......
...@@ -9,37 +9,49 @@ ...@@ -9,37 +9,49 @@
// except according to those terms. // except according to those terms.
use super::{Region, RegionIndex}; use super::{Region, RegionIndex};
use std::mem; use super::free_regions::FreeRegions;
use rustc::infer::InferCtxt; use rustc::infer::InferCtxt;
use rustc::mir::{Location, Mir}; use rustc::mir::{Location, Mir};
use rustc_data_structures::indexed_vec::IndexVec; use rustc::ty;
use rustc_data_structures::indexed_vec::{Idx, IndexVec};
use rustc_data_structures::fx::FxHashSet; use rustc_data_structures::fx::FxHashSet;
pub struct RegionInferenceContext { pub struct RegionInferenceContext<'tcx> {
/// Contains the definition for every region variable. Region /// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionIndex`). The /// variables are identified by their index (`RegionIndex`). The
/// definition contains information about where the region came /// definition contains information about where the region came
/// from as well as its final inferred value. /// from as well as its final inferred value.
definitions: IndexVec<RegionIndex, RegionDefinition>, definitions: IndexVec<RegionIndex, RegionDefinition<'tcx>>,
/// The indices of all "free regions" in scope. These are the
/// lifetime parameters (anonymous and named) declared in the
/// function signature:
///
/// fn foo<'a, 'b>(x: &Foo<'a, 'b>)
/// ^^ ^^ ^
///
/// These indices will be from 0..N, as it happens, but we collect
/// them into a vector for convenience.
free_regions: Vec<RegionIndex>,
/// The constraints we have accumulated and used during solving. /// The constraints we have accumulated and used during solving.
constraints: Vec<Constraint>, constraints: Vec<Constraint>,
/// List of errors we have accumulated as we add constraints.
/// After solving is done, this is replaced with an empty vector.
errors: Vec<InferenceError>,
}
pub struct InferenceError {
pub constraint_point: Location,
pub name: (), // FIXME(nashenas88) RegionName
} }
#[derive(Default)] #[derive(Default)]
struct RegionDefinition { struct RegionDefinition<'tcx> {
name: (), // FIXME(nashenas88) RegionName /// If this is a free-region, then this is `Some(X)` where `X` is
/// the name of the region.
name: Option<ty::Region<'tcx>>,
/// If true, this is a constant region which cannot grow larger.
/// This is used for named regions as well as `'static`.
constant: bool,
/// The current value of this inference variable. This starts out
/// empty, but grows as we add constraints. The final value is
/// determined when `solve()` is executed.
value: Region, value: Region,
capped: bool,
} }
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)] #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
...@@ -49,14 +61,73 @@ pub struct Constraint { ...@@ -49,14 +61,73 @@ pub struct Constraint {
point: Location, point: Location,
} }
impl RegionInferenceContext { impl<'tcx> RegionInferenceContext<'tcx> {
pub fn new(num_region_variables: usize) -> Self { /// Creates a new region inference context with a total of
Self { /// `num_region_variables` valid inference variables; the first N
/// of those will be constant regions representing the free
/// regions defined in `free_regions`.
pub fn new(free_regions: &FreeRegions<'tcx>,
num_region_variables: usize,
mir: &Mir<'tcx>)
-> Self {
let mut result = Self {
definitions: (0..num_region_variables) definitions: (0..num_region_variables)
.map(|_| RegionDefinition::default()) .map(|_| RegionDefinition::default())
.collect(), .collect(),
constraints: Vec::new(), constraints: Vec::new(),
errors: Vec::new(), free_regions: Vec::new(),
};
result.init_free_regions(free_regions, mir);
result
}
fn init_free_regions(&mut self,
free_regions: &FreeRegions<'tcx>,
mir: &Mir<'tcx>)
{
let &FreeRegions { ref indices, ref free_region_map } = free_regions;
// For each free region variable X, it should contain:
//
// (a) the entire CFG
// (b) `end(Y)` for all regions Y such that X: Y (or Y <= X)
//
// we add however the regions for clause (b) somewhat in
// reverse, because of how the data structure in
// `free_regions` is organized.
for (free_region, index) in indices {
let variable = RegionIndex::new(*index);
self.free_regions.push(variable);
self.definitions[variable].name = Some(free_region);
self.definitions[variable].constant = true;
// Add all nodes in the CFG to `definition.value`.
for (block, block_data) in mir.basic_blocks().iter_enumerated() {
let definition = &mut self.definitions[variable];
for statement_index in 0 .. block_data.statements.len() + 1 {
let location = Location { block, statement_index };
definition.value.add_point(location);
}
}
// Add `end(X)` into the set for X.
self.definitions[variable].value.add_free_region(variable);
// Go through each region Y that outlives X (i.e., where
// Y: X is true). Add `end(X)` into the set for `Y`.
for superregion in free_region_map.regions_that_outlive(&free_region) {
let superregion_index = RegionIndex::new(indices[superregion]);
self.definitions[superregion_index].value.add_free_region(variable);
}
debug!("init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`",
free_region,
variable,
self.definitions[variable].value);
} }
} }
...@@ -72,42 +143,25 @@ pub fn region_value(&self, r: RegionIndex) -> &Region { ...@@ -72,42 +143,25 @@ pub fn region_value(&self, r: RegionIndex) -> &Region {
&self.definitions[r].value &self.definitions[r].value
} }
/// Flags a region as being "capped" -- this means that if its /// Indicates that the region variable `v` is live at the point `point`.
/// value is required to grow as a result of some constraint
/// (e.g., `add_live_point` or `add_outlives`), that indicates an
/// error. This is used for the regions representing named
/// lifetime parameters on a function: they get initialized to
/// their complete value, and then "capped" so that they can no
/// longer grow.
#[allow(dead_code)]
pub(super) fn cap_var(&mut self, v: RegionIndex) {
self.definitions[v].capped = true;
}
pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) { pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) {
debug!("add_live_point({:?}, {:?})", v, point); debug!("add_live_point({:?}, {:?})", v, point);
let definition = &mut self.definitions[v]; let definition = &mut self.definitions[v];
if definition.value.add_point(point) { definition.value.add_point(point);
if definition.capped {
self.errors.push(InferenceError {
constraint_point: point,
name: definition.name,
});
}
}
} }
/// Indicates that the region variable `sup` must outlive `sub` is live at the point `point`.
pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) { pub(super) fn add_outlives(&mut self, sup: RegionIndex, sub: RegionIndex, point: Location) {
debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point); debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
self.constraints.push(Constraint { sup, sub, point }); self.constraints.push(Constraint { sup, sub, point });
} }
/// Perform region inference. /// Perform region inference.
pub(super) fn solve<'a, 'gcx, 'tcx>( pub(super) fn solve<'a, 'gcx>(
&mut self, &mut self,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>, mir: &'a Mir<'tcx>,
) -> Vec<InferenceError> )
where where
'gcx: 'tcx + 'a, 'gcx: 'tcx + 'a,
'tcx: 'a, 'tcx: 'a,
...@@ -125,23 +179,6 @@ pub(super) fn solve<'a, 'gcx, 'tcx>( ...@@ -125,23 +179,6 @@ pub(super) fn solve<'a, 'gcx, 'tcx>(
if dfs.copy(sub, &mut sup_def.value, constraint.point) { if dfs.copy(sub, &mut sup_def.value, constraint.point) {
changed = true; changed = true;
if sup_def.capped {
// This is kind of a hack, but when we add a
// constraint, the "point" is always the point
// AFTER the action that induced the
// constraint. So report the error on the
// action BEFORE that.
assert!(constraint.point.statement_index > 0);
let p = Location {
block: constraint.point.block,
statement_index: constraint.point.statement_index - 1,
};
self.errors.push(InferenceError {
constraint_point: p,
name: sup_def.name,
});
}
} }
debug!(" sup (after) : {:?}", sup_def.value); debug!(" sup (after) : {:?}", sup_def.value);
...@@ -149,8 +186,6 @@ pub(super) fn solve<'a, 'gcx, 'tcx>( ...@@ -149,8 +186,6 @@ pub(super) fn solve<'a, 'gcx, 'tcx>(
} }
debug!("\n"); debug!("\n");
} }
mem::replace(&mut self.errors, Vec::new())
} }
} }
...@@ -179,7 +214,7 @@ fn copy( ...@@ -179,7 +214,7 @@ fn copy(
while let Some(p) = stack.pop() { while let Some(p) = stack.pop() {
debug!(" dfs: p={:?}", p); debug!(" dfs: p={:?}", p);
if !from_region.may_contain(p) { if !from_region.may_contain_point(p) {
debug!(" not in from-region"); debug!(" not in from-region");
continue; continue;
} }
...@@ -214,19 +249,11 @@ fn copy( ...@@ -214,19 +249,11 @@ fn copy(
}; };
if successor_points.is_empty() { if successor_points.is_empty() {
// FIXME handle free regions
// If we reach the END point in the graph, then copy // If we reach the END point in the graph, then copy
// over any skolemized end points in the `from_region` // over any skolemized end points in the `from_region`
// and make sure they are included in the `to_region`. // and make sure they are included in the `to_region`.
// for region_decl in self.infcx.tcx.tables.borrow().free_region_map() {
// // FIXME(nashenas88) figure out skolemized_end points to_region.free_regions.extend(&from_region.free_regions);
// let block = self.env.graph.skolemized_end(region_decl.name);
// let skolemized_end_point = Location {
// block,
// statement_index: 0,
// };
// changed |= to_region.add_point(skolemized_end_point);
// }
} else { } else {
stack.extend(successor_points); stack.extend(successor_points);
} }
......
...@@ -8,22 +8,40 @@ ...@@ -8,22 +8,40 @@
// option. This file may not be copied, modified, or distributed // option. This file may not be copied, modified, or distributed
// except according to those terms. // except according to those terms.
use rustc::ty::TypeFoldable; use rustc_data_structures::indexed_vec::Idx;
use rustc::ty::subst::{Kind, Substs}; use rustc::ty::subst::{Kind, Substs};
use rustc::ty::{Ty, ClosureSubsts, RegionVid, RegionKind}; use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable};
use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind}; use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, TyContext}; use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{self as rustc_infer, InferCtxt}; use rustc::infer::{self as rustc_infer, InferCtxt};
use syntax_pos::DUMMY_SP; use syntax_pos::DUMMY_SP;
use std::collections::HashMap; use std::collections::HashMap;
use super::free_regions::FreeRegions;
/// Replaces all free regions appearing in the MIR with fresh /// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created. /// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>, pub fn renumber_mir<'a, 'gcx, 'tcx>(
mir: &mut Mir<'tcx>) infcx: &InferCtxt<'a, 'gcx, 'tcx>,
-> usize free_regions: &FreeRegions<'tcx>,
{ mir: &mut Mir<'tcx>,
let mut visitor = NLLVisitor::new(infcx); ) -> usize {
// Create inference variables for each of the free regions
// declared on the function signature.
let free_region_inference_vars = (0..free_regions.indices.len())
.map(|_| {
infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
})
.collect();
let mut visitor = NLLVisitor {
infcx,
lookup_map: HashMap::new(),
num_region_variables: free_regions.indices.len(),
free_regions,
free_region_inference_vars,
arg_count: mir.arg_count,
};
visitor.visit_mir(mir); visitor.visit_mir(mir);
visitor.num_region_variables visitor.num_region_variables
} }
...@@ -32,22 +50,40 @@ struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> { ...@@ -32,22 +50,40 @@ struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
lookup_map: HashMap<RegionVid, TyContext>, lookup_map: HashMap<RegionVid, TyContext>,
num_region_variables: usize, num_region_variables: usize,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>, infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
free_regions: &'a FreeRegions<'tcx>,
free_region_inference_vars: Vec<ty::Region<'tcx>>,
arg_count: usize,
} }
impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self { /// Replaces all regions appearing in `value` with fresh inference
NLLVisitor { /// variables. This is what we do for almost the entire MIR, with
infcx, /// the exception of the declared types of our arguments.
lookup_map: HashMap::new(), fn renumber_regions<T>(&mut self, value: &T) -> T
num_region_variables: 0 where
} T: TypeFoldable<'tcx>,
{
self.infcx
.tcx
.fold_regions(value, &mut false, |_region, _depth| {
self.num_region_variables += 1;
self.infcx
.next_region_var(rustc_infer::MiscVariable(DUMMY_SP))
})
} }
fn renumber_regions<T>(&mut self, value: &T) -> T where T: TypeFoldable<'tcx> { /// Renumbers the regions appearing in `value`, but those regions
self.infcx.tcx.fold_regions(value, &mut false, |_region, _depth| { /// are expected to be free regions from the function signature.
self.num_region_variables += 1; fn renumber_free_regions<T>(&mut self, value: &T) -> T
self.infcx.next_region_var(rustc_infer::MiscVariable(DUMMY_SP)) where
}) T: TypeFoldable<'tcx>,
{
self.infcx
.tcx
.fold_regions(value, &mut false, |region, _depth| {
let index = self.free_regions.indices[&region];
self.free_region_inference_vars[index]
})
} }
fn store_region(&mut self, region: &RegionKind, lookup: TyContext) { fn store_region(&mut self, region: &RegionKind, lookup: TyContext) {
...@@ -69,17 +105,31 @@ fn store_kind_regions(&mut self, kind: &'tcx Kind, ty_context: TyContext) { ...@@ -69,17 +105,31 @@ fn store_kind_regions(&mut self, kind: &'tcx Kind, ty_context: TyContext) {
self.store_region(region, ty_context); self.store_region(region, ty_context);
} }
} }
fn is_argument_or_return_slot(&self, local: Local) -> bool {
// The first argument is return slot, next N are arguments.
local.index() <= self.arg_count
}
} }
impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> { impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) { fn visit_ty(&mut self, ty: &mut Ty<'tcx>, ty_context: TyContext) {
let is_arg = match ty_context {
TyContext::LocalDecl { local, .. } => self.is_argument_or_return_slot(local),
_ => false,
};
let old_ty = *ty; let old_ty = *ty;
*ty = self.renumber_regions(&old_ty); *ty = if is_arg {
self.renumber_free_regions(&old_ty)
} else {
self.renumber_regions(&old_ty)
};
self.store_ty_regions(ty, ty_context); self.store_ty_regions(ty, ty_context);
} }
fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) { fn visit_substs(&mut self, substs: &mut &'tcx Substs<'tcx>, location: Location) {
*substs = self.renumber_regions(&{*substs}); *substs = self.renumber_regions(&{ *substs });
let ty_context = TyContext::Location(location); let ty_context = TyContext::Location(location);
for kind in *substs { for kind in *substs {
self.store_kind_regions(kind, ty_context); self.store_kind_regions(kind, ty_context);
...@@ -110,9 +160,7 @@ fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) { ...@@ -110,9 +160,7 @@ fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
self.super_rvalue(rvalue, location); self.super_rvalue(rvalue, location);
} }
fn visit_closure_substs(&mut self, fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
substs: &mut ClosureSubsts<'tcx>,
location: Location) {
*substs = self.renumber_regions(substs); *substs = self.renumber_regions(substs);
let ty_context = TyContext::Location(location); let ty_context = TyContext::Location(location);
for kind in substs.substs { for kind in substs.substs {
...@@ -120,10 +168,12 @@ fn visit_closure_substs(&mut self, ...@@ -120,10 +168,12 @@ fn visit_closure_substs(&mut self,
} }
} }
fn visit_statement(&mut self, fn visit_statement(
block: BasicBlock, &mut self,
statement: &mut Statement<'tcx>, block: BasicBlock,
location: Location) { statement: &mut Statement<'tcx>,
location: Location,
) {
if let StatementKind::EndRegion(_) = statement.kind { if let StatementKind::EndRegion(_) = statement.kind {
statement.kind = StatementKind::Nop; statement.kind = StatementKind::Nop;
} }
......
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
&*x
}
fn main() { }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册