提交 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> {
location_map: FxHashMap<Location, BorrowIndex>,
region_map: FxHashMap<Region<'tcx>, FxHashSet<BorrowIndex>>,
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
......@@ -69,7 +69,7 @@ fn fmt(&self, w: &mut fmt::Formatter) -> fmt::Result {
impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
pub fn new(tcx: TyCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
nonlexical_regioncx: Option<&'a RegionInferenceContext>)
nonlexical_regioncx: Option<&'a RegionInferenceContext<'tcx>>)
-> Self {
let mut visitor = GatherBorrows { idx_vec: IndexVec::new(),
location_map: FxHashMap(),
......@@ -140,7 +140,7 @@ fn kill_loans_out_of_scope_at_location(&self,
if let Some(regioncx) = self.nonlexical_regioncx {
for (borrow_index, borrow_data) in self.borrows.iter_enumerated() {
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{:?} \
location={:?} borrow_data={:?}", borrow_index, location, borrow_data);
sets.kill(&borrow_index);
......
......@@ -29,7 +29,7 @@
pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
regioncx: &mut RegionInferenceContext,
regioncx: &mut RegionInferenceContext<'tcx>,
mir: &Mir<'tcx>,
mir_source: MirSource,
liveness: &LivenessResults,
......@@ -45,7 +45,7 @@ pub(super) fn generate_constraints<'a, 'gcx, 'tcx>(
struct ConstraintGeneration<'cx, 'gcx: 'tcx, 'tcx: 'cx> {
infcx: &'cx InferCtxt<'cx, 'gcx, 'tcx>,
regioncx: &'cx mut RegionInferenceContext,
regioncx: &'cx mut RegionInferenceContext<'tcx>,
mir: &'cx Mir<'tcx>,
liveness: &'cx LivenessResults,
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 @@
use self::mir_util::PassWhere;
mod constraint_generation;
mod free_regions;
mod subtype;
pub(crate) mod region_infer;
......@@ -36,9 +37,12 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
source: MirSource,
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.
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.
let liveness = &LivenessResults {
......@@ -61,11 +65,9 @@ pub fn compute_regions<'a, 'gcx, 'tcx>(
// Create the region inference context, generate the constraints,
// 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);
let errors = regioncx.solve(infcx, &mir);
assert!(errors.is_empty(), "FIXME: report region inference failures");
regioncx.solve(infcx, &mir);
// Dump MIR results into a file, if that is enabled. This let us
// write unit-tests.
......@@ -152,11 +154,15 @@ fn dump_mir_results<'a, 'gcx, 'tcx>(
#[derive(Clone, Default, PartialEq, Eq)]
pub struct Region {
points: BTreeSet<Location>,
free_regions: BTreeSet<RegionIndex>,
}
impl fmt::Debug for Region {
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 {
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)
}
pub fn may_contain_free_region(&self, region: RegionIndex) -> bool {
self.free_regions.contains(&region)
}
}
newtype_index!(RegionIndex {
......
......@@ -9,37 +9,49 @@
// except according to those terms.
use super::{Region, RegionIndex};
use std::mem;
use super::free_regions::FreeRegions;
use rustc::infer::InferCtxt;
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;
pub struct RegionInferenceContext {
pub struct RegionInferenceContext<'tcx> {
/// Contains the definition for every region variable. Region
/// variables are identified by their index (`RegionIndex`). The
/// definition contains information about where the region came
/// 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.
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)]
struct RegionDefinition {
name: (), // FIXME(nashenas88) RegionName
struct RegionDefinition<'tcx> {
/// 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,
capped: bool,
}
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
......@@ -49,14 +61,73 @@ pub struct Constraint {
point: Location,
}
impl RegionInferenceContext {
pub fn new(num_region_variables: usize) -> Self {
Self {
impl<'tcx> RegionInferenceContext<'tcx> {
/// Creates a new region inference context with a total of
/// `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)
.map(|_| RegionDefinition::default())
.collect(),
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 {
&self.definitions[r].value
}
/// Flags a region as being "capped" -- this means that if its
/// 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;
}
/// Indicates that the region variable `v` is live at the point `point`.
pub(super) fn add_live_point(&mut self, v: RegionIndex, point: Location) {
debug!("add_live_point({:?}, {:?})", v, point);
let definition = &mut self.definitions[v];
if definition.value.add_point(point) {
if definition.capped {
self.errors.push(InferenceError {
constraint_point: point,
name: definition.name,
});
}
}
definition.value.add_point(point);
}
/// 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) {
debug!("add_outlives({:?}: {:?} @ {:?}", sup, sub, point);
self.constraints.push(Constraint { sup, sub, point });
}
/// Perform region inference.
pub(super) fn solve<'a, 'gcx, 'tcx>(
pub(super) fn solve<'a, 'gcx>(
&mut self,
infcx: &'a InferCtxt<'a, 'gcx, 'tcx>,
mir: &'a Mir<'tcx>,
) -> Vec<InferenceError>
)
where
'gcx: 'tcx + 'a,
'tcx: 'a,
......@@ -125,23 +179,6 @@ pub(super) fn solve<'a, 'gcx, 'tcx>(
if dfs.copy(sub, &mut sup_def.value, constraint.point) {
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);
......@@ -149,8 +186,6 @@ pub(super) fn solve<'a, 'gcx, 'tcx>(
}
debug!("\n");
}
mem::replace(&mut self.errors, Vec::new())
}
}
......@@ -179,7 +214,7 @@ fn copy(
while let Some(p) = stack.pop() {
debug!(" dfs: p={:?}", p);
if !from_region.may_contain(p) {
if !from_region.may_contain_point(p) {
debug!(" not in from-region");
continue;
}
......@@ -214,19 +249,11 @@ fn copy(
};
if successor_points.is_empty() {
// FIXME handle free regions
// If we reach the END point in the graph, then copy
// over any skolemized end points in the `from_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
// 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);
// }
to_region.free_regions.extend(&from_region.free_regions);
} else {
stack.extend(successor_points);
}
......
......@@ -8,22 +8,40 @@
// option. This file may not be copied, modified, or distributed
// 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::{Ty, ClosureSubsts, RegionVid, RegionKind};
use rustc::mir::{Mir, Location, Rvalue, BasicBlock, Statement, StatementKind};
use rustc::ty::{self, ClosureSubsts, RegionKind, RegionVid, Ty, TypeFoldable};
use rustc::mir::{BasicBlock, Local, Location, Mir, Rvalue, Statement, StatementKind};
use rustc::mir::visit::{MutVisitor, TyContext};
use rustc::infer::{self as rustc_infer, InferCtxt};
use syntax_pos::DUMMY_SP;
use std::collections::HashMap;
use super::free_regions::FreeRegions;
/// Replaces all free regions appearing in the MIR with fresh
/// inference variables, returning the number of variables created.
pub fn renumber_mir<'a, 'gcx, 'tcx>(infcx: &InferCtxt<'a, 'gcx, 'tcx>,
mir: &mut Mir<'tcx>)
-> usize
{
let mut visitor = NLLVisitor::new(infcx);
pub fn renumber_mir<'a, 'gcx, 'tcx>(
infcx: &InferCtxt<'a, 'gcx, 'tcx>,
free_regions: &FreeRegions<'tcx>,
mir: &mut Mir<'tcx>,
) -> 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.num_region_variables
}
......@@ -32,22 +50,40 @@ struct NLLVisitor<'a, 'gcx: 'a + 'tcx, 'tcx: 'a> {
lookup_map: HashMap<RegionVid, TyContext>,
num_region_variables: usize,
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> {
pub fn new(infcx: &'a InferCtxt<'a, 'gcx, 'tcx>) -> Self {
NLLVisitor {
infcx,
lookup_map: HashMap::new(),
num_region_variables: 0
}
/// Replaces all regions appearing in `value` with fresh inference
/// variables. This is what we do for almost the entire MIR, with
/// the exception of the declared types of our arguments.
fn renumber_regions<T>(&mut self, value: &T) -> T
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> {
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))
})
/// Renumbers the regions appearing in `value`, but those regions
/// are expected to be free regions from the function signature.
fn renumber_free_regions<T>(&mut self, value: &T) -> T
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) {
......@@ -69,17 +105,31 @@ fn store_kind_regions(&mut self, kind: &'tcx Kind, ty_context: TyContext) {
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> {
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;
*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);
}
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);
for kind in *substs {
self.store_kind_regions(kind, ty_context);
......@@ -110,9 +160,7 @@ fn visit_rvalue(&mut self, rvalue: &mut Rvalue<'tcx>, location: Location) {
self.super_rvalue(rvalue, location);
}
fn visit_closure_substs(&mut self,
substs: &mut ClosureSubsts<'tcx>,
location: Location) {
fn visit_closure_substs(&mut self, substs: &mut ClosureSubsts<'tcx>, location: Location) {
*substs = self.renumber_regions(substs);
let ty_context = TyContext::Location(location);
for kind in substs.substs {
......@@ -120,10 +168,12 @@ fn visit_closure_substs(&mut self,
}
}
fn visit_statement(&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location) {
fn visit_statement(
&mut self,
block: BasicBlock,
statement: &mut Statement<'tcx>,
location: Location,
) {
if let StatementKind::EndRegion(_) = statement.kind {
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.
先完成此消息的编辑!
想要评论请 注册