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

replace constant regions with a post-inference check

Rather than declaring some region variables to be constant, and
reporting errors when they would have to change, we instead populate
each free region X with a minimal set of points (the CFG plus end(X)),
and then we let inference do its thing. This may add other `end(Y)`
points into X; we can then check after the fact that indeed `X: Y`
holds.

This requires a bit of "blame" detection to find where the bad
constraint came from: we are currently using a pretty dumb
algorithm. Good place for later expansion.
上级 932452ec
...@@ -63,28 +63,28 @@ pub fn is_subregion_of(&self, ...@@ -63,28 +63,28 @@ pub fn is_subregion_of(&self,
-> bool { -> bool {
let result = sub_region == super_region || { let result = sub_region == super_region || {
match (sub_region, super_region) { match (sub_region, super_region) {
(&ty::ReEmpty, _) | (ty::ReEmpty, _) |
(_, &ty::ReStatic) => (_, ty::ReStatic) =>
true, true,
(&ty::ReScope(sub_scope), &ty::ReScope(super_scope)) => (ty::ReScope(sub_scope), ty::ReScope(super_scope)) =>
self.region_scope_tree.is_subscope_of(sub_scope, super_scope), self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope),
(&ty::ReScope(sub_scope), &ty::ReEarlyBound(ref br)) => { (ty::ReScope(sub_scope), ty::ReEarlyBound(ref br)) => {
let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br); let fr_scope = self.region_scope_tree.early_free_scope(self.tcx, br);
self.region_scope_tree.is_subscope_of(sub_scope, fr_scope) self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
} }
(&ty::ReScope(sub_scope), &ty::ReFree(ref fr)) => { (ty::ReScope(sub_scope), ty::ReFree(fr)) => {
let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr); let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
self.region_scope_tree.is_subscope_of(sub_scope, fr_scope) self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
} }
(&ty::ReEarlyBound(_), &ty::ReEarlyBound(_)) | (ty::ReEarlyBound(_), ty::ReEarlyBound(_)) |
(&ty::ReFree(_), &ty::ReEarlyBound(_)) | (ty::ReFree(_), ty::ReEarlyBound(_)) |
(&ty::ReEarlyBound(_), &ty::ReFree(_)) | (ty::ReEarlyBound(_), ty::ReFree(_)) |
(&ty::ReFree(_), &ty::ReFree(_)) => (ty::ReFree(_), ty::ReFree(_)) =>
self.free_regions.sub_free_regions(&sub_region, &super_region), self.free_regions.sub_free_regions(sub_region, super_region),
_ => _ =>
false, false,
...@@ -162,23 +162,23 @@ pub fn relate_free_regions_from_predicates(&mut self, ...@@ -162,23 +162,23 @@ pub fn relate_free_regions_from_predicates(&mut self,
/// (with the exception that `'static: 'x` is not notable) /// (with the exception that `'static: 'x` is not notable)
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) { pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
debug!("relate_regions(sub={:?}, sup={:?})", sub, sup); debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
if (is_free(sub) || *sub == ty::ReStatic) && is_free(sup) { if is_free_or_static(sub) && is_free(sup) {
self.relation.add(sub, sup) self.relation.add(sub, sup)
} }
} }
/// True if `r_a <= r_b` is known to hold. Both `r_a` and `r_b` /// Tests whether `r_a <= sup`. Both must be free regions or
/// must be free regions from the function header. /// `'static`.
pub fn sub_free_regions<'a, 'gcx>(&self, pub fn sub_free_regions<'a, 'gcx>(&self,
r_a: Region<'tcx>, r_a: Region<'tcx>,
r_b: Region<'tcx>) r_b: Region<'tcx>)
-> bool { -> bool {
debug!("sub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b); assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
assert!(is_free(r_a)); if let ty::ReStatic = r_b {
assert!(is_free(r_b)); true // `'a <= 'static` is just always true, and not stored in the relation explicitly
let result = r_a == r_b || self.relation.contains(&r_a, &r_b); } else {
debug!("sub_free_regions: result={}", result); r_a == r_b || self.relation.contains(&r_a, &r_b)
result }
} }
/// Compute the least-upper-bound of two free regions. In some /// Compute the least-upper-bound of two free regions. In some
...@@ -224,6 +224,13 @@ fn is_free(r: Region) -> bool { ...@@ -224,6 +224,13 @@ fn is_free(r: Region) -> bool {
} }
} }
fn is_free_or_static(r: Region) -> bool {
match *r {
ty::ReStatic => true,
_ => is_free(r),
}
}
impl_stable_hash_for!(struct FreeRegionMap<'tcx> { impl_stable_hash_for!(struct FreeRegionMap<'tcx> {
relation relation
}); });
......
...@@ -13,6 +13,7 @@ ...@@ -13,6 +13,7 @@
use rustc::infer::RegionVariableOrigin; use rustc::infer::RegionVariableOrigin;
use rustc::infer::NLLRegionVariableOrigin; use rustc::infer::NLLRegionVariableOrigin;
use rustc::infer::region_constraints::VarOrigins; use rustc::infer::region_constraints::VarOrigins;
use rustc::middle::free_region::FreeRegionMap;
use rustc::mir::{Location, Mir}; use rustc::mir::{Location, Mir};
use rustc::ty::{self, RegionVid}; use rustc::ty::{self, RegionVid};
use rustc_data_structures::indexed_vec::IndexVec; use rustc_data_structures::indexed_vec::IndexVec;
...@@ -40,6 +41,8 @@ pub struct RegionInferenceContext<'tcx> { ...@@ -40,6 +41,8 @@ pub struct RegionInferenceContext<'tcx> {
/// The constraints we have accumulated and used during solving. /// The constraints we have accumulated and used during solving.
constraints: Vec<Constraint>, constraints: Vec<Constraint>,
free_region_map: &'tcx FreeRegionMap<'tcx>,
} }
struct RegionDefinition<'tcx> { struct RegionDefinition<'tcx> {
...@@ -52,10 +55,6 @@ struct RegionDefinition<'tcx> { ...@@ -52,10 +55,6 @@ struct RegionDefinition<'tcx> {
/// If this is a free-region, then this is `Some(X)` where `X` is /// If this is a free-region, then this is `Some(X)` where `X` is
/// the name of the region. /// the name of the region.
name: Option<ty::Region<'tcx>>, 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 value of an individual region variable. Region variables /// The value of an individual region variable. Region variables
...@@ -100,7 +99,6 @@ pub struct Constraint { ...@@ -100,7 +99,6 @@ pub struct Constraint {
// it is for convenience. Before we dump the constraints in the // it is for convenience. Before we dump the constraints in the
// debugging logs, we sort them, and we'd like the "super region" // debugging logs, we sort them, and we'd like the "super region"
// to be first, etc. (In particular, span should remain last.) // to be first, etc. (In particular, span should remain last.)
/// The region SUP must outlive SUB... /// The region SUP must outlive SUB...
sup: RegionVid, sup: RegionVid,
...@@ -114,7 +112,7 @@ pub struct Constraint { ...@@ -114,7 +112,7 @@ pub struct Constraint {
span: Span, span: Span,
} }
impl<'a, 'gcx, 'tcx> RegionInferenceContext<'tcx> { impl<'tcx> RegionInferenceContext<'tcx> {
/// Creates a new region inference context with a total of /// Creates a new region inference context with a total of
/// `num_region_variables` valid inference variables; the first N /// `num_region_variables` valid inference variables; the first N
/// of those will be constant regions representing the free /// of those will be constant regions representing the free
...@@ -133,6 +131,7 @@ pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir< ...@@ -133,6 +131,7 @@ pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<
liveness_constraints: IndexVec::from_elem_n(Region::default(), num_region_variables), liveness_constraints: IndexVec::from_elem_n(Region::default(), num_region_variables),
inferred_values: None, inferred_values: None,
constraints: Vec::new(), constraints: Vec::new(),
free_region_map: free_regions.free_region_map,
}; };
result.init_free_regions(free_regions, mir); result.init_free_regions(free_regions, mir);
...@@ -162,7 +161,7 @@ pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir< ...@@ -162,7 +161,7 @@ pub fn new(var_origins: VarOrigins, free_regions: &FreeRegions<'tcx>, mir: &Mir<
fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) { fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx>) {
let FreeRegions { let FreeRegions {
indices, indices,
free_region_map, free_region_map: _,
} = free_regions; } = free_regions;
// For each free region X: // For each free region X:
...@@ -175,7 +174,6 @@ fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx ...@@ -175,7 +174,6 @@ fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx
// Initialize the name and a few other details. // Initialize the name and a few other details.
self.definitions[variable].name = Some(free_region); self.definitions[variable].name = Some(free_region);
self.definitions[variable].constant = true;
// Add all nodes in the CFG to `definition.value`. // Add all nodes in the CFG to `definition.value`.
for (block, block_data) in mir.basic_blocks().iter_enumerated() { for (block, block_data) in mir.basic_blocks().iter_enumerated() {
...@@ -192,21 +190,6 @@ fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx ...@@ -192,21 +190,6 @@ fn init_free_regions(&mut self, free_regions: &FreeRegions<'tcx>, mir: &Mir<'tcx
// Add `end(X)` into the set for X. // Add `end(X)` into the set for X.
self.liveness_constraints[variable].add_free_region(variable); self.liveness_constraints[variable].add_free_region(variable);
// `'static` outlives all other free regions as well.
if let ty::ReStatic = free_region {
for &other_variable in indices.values() {
self.liveness_constraints[variable]
.add_free_region(other_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 = indices[superregion];
self.liveness_constraints[superregion_index].add_free_region(variable);
}
debug!( debug!(
"init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`", "init_free_regions: region variable for `{:?}` is `{:?}` with value `{:?}`",
free_region, free_region,
...@@ -265,20 +248,72 @@ pub(super) fn add_outlives( ...@@ -265,20 +248,72 @@ pub(super) fn add_outlives(
} }
/// Perform region inference. /// Perform region inference.
pub(super) fn solve(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx>) { pub(super) fn solve(&mut self, infcx: &InferCtxt<'_, '_, 'tcx>, mir: &Mir<'tcx>) {
assert!(self.inferred_values.is_none(), "values already inferred"); assert!(self.inferred_values.is_none(), "values already inferred");
let errors = self.propagate_constraints(mir);
// Find the minimal regions that can solve the constraints. This is infallible.
// worst error msg ever self.propagate_constraints(mir);
for (fr1, span, fr2) in errors {
infcx.tcx.sess.span_err( // Now, see whether any of the constraints were too strong. In particular,
span, // we want to check for a case where a free region exceeded its bounds.
&format!( // Consider:
"free region `{}` does not outlive `{}`", //
self.definitions[fr1].name.unwrap(), // fn foo<'a, 'b>(x: &'a u32) -> &'b u32 { x }
self.definitions[fr2].name.unwrap() //
), // In this case, returning `x` requires `&'a u32 <: &'b u32`
); // and hence we establish (transitively) a constraint that
// `'a: 'b`. The `propagate_constraints` code above will
// therefore add `end('a)` into the region for `'b` -- but we
// have no evidence that `'b` outlives `'a`, so we want to report
// an error.
// The free regions are always found in a prefix of the full list.
let free_region_definitions = self.definitions
.iter_enumerated()
.take_while(|(_, fr_definition)| fr_definition.name.is_some());
for (fr, fr_definition) in free_region_definitions {
self.check_free_region(infcx, fr, fr_definition);
}
}
fn check_free_region(
&self,
infcx: &InferCtxt<'_, '_, 'tcx>,
fr: RegionVid,
fr_definition: &RegionDefinition<'tcx>,
) {
let inferred_values = self.inferred_values.as_ref().unwrap();
let fr_name = fr_definition.name.unwrap();
let fr_value = &inferred_values[fr];
// Find every region `o` such that `fr: o`
// (because `fr` includes `end(o)`).
for &outlived_fr in &fr_value.free_regions {
// `fr` includes `end(fr)`, that's not especially
// interesting.
if fr == outlived_fr {
continue;
}
let outlived_fr_definition = &self.definitions[outlived_fr];
let outlived_fr_name = outlived_fr_definition.name.unwrap();
// Check that `o <= fr`. If not, report an error.
if !self.free_region_map
.sub_free_regions(outlived_fr_name, fr_name)
{
// worst error msg ever
let blame_span = self.blame_span(fr, outlived_fr);
infcx.tcx.sess.span_err(
blame_span,
&format!(
"free region `{}` does not outlive `{}`",
fr_name,
outlived_fr_name
),
);
}
} }
} }
...@@ -286,11 +321,9 @@ pub(super) fn solve(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx ...@@ -286,11 +321,9 @@ pub(super) fn solve(&mut self, infcx: &InferCtxt<'a, 'gcx, 'tcx>, mir: &Mir<'tcx
/// for each region variable until all the constraints are /// for each region variable until all the constraints are
/// satisfied. Note that some values may grow **too** large to be /// satisfied. Note that some values may grow **too** large to be
/// feasible, but we check this later. /// feasible, but we check this later.
fn propagate_constraints(&mut self, mir: &Mir<'tcx>) -> Vec<(RegionVid, Span, RegionVid)> { fn propagate_constraints(&mut self, mir: &Mir<'tcx>) {
let mut changed = true; let mut changed = true;
let mut dfs = Dfs::new(mir); let mut dfs = Dfs::new(mir);
let mut error_regions = FxHashSet();
let mut errors = vec![];
debug!("propagate_constraints()"); debug!("propagate_constraints()");
debug!("propagate_constraints: constraints={:#?}", { debug!("propagate_constraints: constraints={:#?}", {
...@@ -305,51 +338,78 @@ fn propagate_constraints(&mut self, mir: &Mir<'tcx>) -> Vec<(RegionVid, Span, Re ...@@ -305,51 +338,78 @@ fn propagate_constraints(&mut self, mir: &Mir<'tcx>) -> Vec<(RegionVid, Span, Re
while changed { while changed {
changed = false; changed = false;
debug!("propagate_constraints: --------------------");
for constraint in &self.constraints { for constraint in &self.constraints {
debug!("propagate_constraints: constraint={:?}", constraint); debug!("propagate_constraints: constraint={:?}", constraint);
let sub = &inferred_values[constraint.sub].clone(); let sub = &inferred_values[constraint.sub].clone();
let sup_value = &mut inferred_values[constraint.sup]; let sup_value = &mut inferred_values[constraint.sup];
debug!("propagate_constraints: sub (before): {:?}", sub); // Grow the value as needed to accommodate the
debug!("propagate_constraints: sup (before): {:?}", sup_value); // outlives constraint.
if !self.definitions[constraint.sup].constant { if dfs.copy(sub, sup_value, constraint.point) {
// If this is not a constant, then grow the value as needed to debug!("propagate_constraints: sub={:?}", sub);
// accommodate the outlives constraint. debug!("propagate_constraints: sup={:?}", sup_value);
changed = true;
}
}
debug!("\n");
}
if dfs.copy(sub, sup_value, constraint.point) { self.inferred_values = Some(inferred_values);
changed = true; }
}
debug!("propagate_constraints: sup (after) : {:?}", sup_value); /// Tries to finds a good span to blame for the fact that `fr1`
debug!("propagate_constraints: changed : {:?}", changed); /// contains `fr2`.
} else { fn blame_span(&self, fr1: RegionVid, fr2: RegionVid) -> Span {
// If this is a constant, check whether it *would // Find everything that influenced final value of `fr`.
// have* to grow in order for the constraint to be let influenced_fr1 = self.dependencies(fr1);
// satisfied. If so, create an error.
// Try to find some outlives constraint `'X: fr2` where `'X`
let sup_value = &mut sup_value.clone(); // influenced `fr1`. Blame that.
if dfs.copy(sub, sup_value, constraint.point) { //
// Constant values start out with the entire // NB, this is a pretty bad choice most of the time. In
// CFG, so it must be some new free region // particular, the connection between `'X` and `fr1` may not
// that was added. Find one. // be obvious to the user -- not to mention the naive notion
let &new_region = sup_value // of dependencies, which doesn't account for the locations of
.free_regions // contraints at all. But it will do for now.
.difference(&sup_value.free_regions) for constraint in &self.constraints {
.next() if constraint.sub == fr2 && influenced_fr1[constraint.sup] {
.unwrap(); return constraint.span;
debug!("propagate_constraints: new_region : {:?}", new_region); }
if error_regions.insert(constraint.sup) { }
errors.push((constraint.sup, constraint.span, new_region));
} bug!(
"could not find any constraint to blame for {:?}: {:?}",
fr1,
fr2
);
}
/// Finds all regions whose values `'a` may depend on in some way.
/// Basically if there exists a constraint `'a: 'b @ P`, then `'b`
/// and `dependencies('b)` will be in the final set.
///
/// Used during error reporting, extremely naive and inefficient.
fn dependencies(&self, r0: RegionVid) -> IndexVec<RegionVid, bool> {
let mut result_set = IndexVec::from_elem(false, &self.definitions);
let mut changed = true;
result_set[r0] = true;
while changed {
changed = false;
for constraint in &self.constraints {
if result_set[constraint.sup] {
if !result_set[constraint.sub] {
result_set[constraint.sub] = true;
changed = true;
} }
} }
} }
debug!("\n");
} }
self.inferred_values = Some(inferred_values); result_set
errors
} }
} }
...@@ -434,11 +494,7 @@ fn new(origin: RegionVariableOrigin) -> Self { ...@@ -434,11 +494,7 @@ fn new(origin: RegionVariableOrigin) -> Self {
// Create a new region definition. Note that, for free // Create a new region definition. Note that, for free
// regions, these fields get updated later in // regions, these fields get updated later in
// `init_free_regions`. // `init_free_regions`.
Self { Self { origin, name: None }
origin,
name: None,
constant: false,
}
} }
} }
......
...@@ -26,9 +26,9 @@ fn main() { ...@@ -26,9 +26,9 @@ fn main() {
// END RUST SOURCE // END RUST SOURCE
// START rustc.use_x.nll.0.mir // START rustc.use_x.nll.0.mir
// | '_#0r: {bb0[0], bb0[1], '_#0r, '_#1r, '_#2r, '_#3r} // | '_#0r: {bb0[0], bb0[1], '_#0r}
// | '_#1r: {bb0[0], bb0[1], '_#1r} // | '_#1r: {bb0[0], bb0[1], '_#1r}
// | '_#2r: {bb0[0], bb0[1], '_#1r, '_#2r} // | '_#2r: {bb0[0], bb0[1], '_#2r}
// | '_#3r: {bb0[0], bb0[1], '_#3r} // | '_#3r: {bb0[0], bb0[1], '_#3r}
// fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool { // fn use_x(_1: &'_#1r mut i32, _2: &'_#2r u32, _3: &'_#1r u32, _4: &'_#3r u32) -> bool {
// END rustc.use_x.nll.0.mir // END rustc.use_x.nll.0.mir
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册