extend NLL regions to include free region indices and add outlives
This commit is contained in:
parent
7b30e8d668
commit
cafbd99c38
7 changed files with 297 additions and 113 deletions
|
|
@ -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 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> {
|
|||
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 @@ impl<'a, 'gcx, 'tcx> Borrows<'a, 'gcx, 'tcx> {
|
|||
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 @@ use super::region_infer::RegionInferenceContext;
|
|||
|
||||
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,
|
||||
|
|
|
|||
88
src/librustc_mir/transform/nll/free_regions.rs
Normal file
88
src/librustc_mir/transform/nll/free_regions.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
|||
// 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 util as mir_util;
|
|||
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 @@ impl Region {
|
|||
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(®ion)
|
||||
}
|
||||
}
|
||||
|
||||
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 @@ impl RegionInferenceContext {
|
|||
&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 @@ impl RegionInferenceContext {
|
|||
|
||||
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 @@ impl RegionInferenceContext {
|
|||
}
|
||||
debug!("\n");
|
||||
}
|
||||
|
||||
mem::replace(&mut self.errors, Vec::new())
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -179,7 +214,7 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
|
|||
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 @@ impl<'a, 'gcx: 'tcx, 'tcx: 'a> Dfs<'a, 'gcx, 'tcx> {
|
|||
};
|
||||
|
||||
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[®ion];
|
||||
self.free_region_inference_vars[index]
|
||||
})
|
||||
}
|
||||
|
||||
fn store_region(&mut self, region: &RegionKind, lookup: TyContext) {
|
||||
|
|
@ -69,17 +105,31 @@ impl<'a, 'gcx, 'tcx> NLLVisitor<'a, 'gcx, 'tcx> {
|
|||
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 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
|
|||
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 @@ impl<'a, 'gcx, 'tcx> MutVisitor<'tcx> for NLLVisitor<'a, 'gcx, 'tcx> {
|
|||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
|
|
|||
5
src/test/compile-fail/nll/region-error.rs
Normal file
5
src/test/compile-fail/nll/region-error.rs
Normal file
|
|
@ -0,0 +1,5 @@
|
|||
fn foo<'a, 'b>(x: &'a u32, y: &'b u32) -> &'b u32 {
|
||||
&*x
|
||||
}
|
||||
|
||||
fn main() { }
|
||||
Loading…
Add table
Add a link
Reference in a new issue