Remove contraction. The contraction rules predated the notion of an

empty region, and they complicate region inference to no particular end.
They also lead in some cases to spurious errors like #29048 (though in
some cases these errors are helpful in tracking down missing
constraints).
This commit is contained in:
Niko Matsakis 2015-10-16 20:19:25 -04:00
parent 60ab57e56d
commit 41bca6dd76
4 changed files with 58 additions and 312 deletions

View File

@ -16,19 +16,16 @@ pub use self::UndoLogEntry::*;
pub use self::CombineMapType::*;
pub use self::RegionResolutionError::*;
pub use self::VarValue::*;
use self::Classification::*;
use super::{RegionVariableOrigin, SubregionOrigin, TypeTrace, MiscVariable};
use rustc_data_structures::graph::{self, Direction, NodeIndex};
use middle::free_region::FreeRegionMap;
use middle::region;
use middle::ty::{self, Ty};
use middle::ty::{BoundRegion, FreeRegion, Region, RegionVid};
use middle::ty::{ReEmpty, ReStatic, ReFree, ReEarlyBound};
use middle::ty::{ReLateBound, ReScope, ReVar, ReSkolemized, BrFresh};
use middle::ty::error::TypeError;
use middle::ty::relate::RelateResult;
use util::common::indenter;
use util::nodemap::{FnvHashMap, FnvHashSet};
@ -824,147 +821,14 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
}
}
fn glb_concrete_regions(&self,
free_regions: &FreeRegionMap,
a: Region,
b: Region)
-> RelateResult<'tcx, Region>
{
debug!("glb_concrete_regions({:?}, {:?})", a, b);
match (a, b) {
(ReLateBound(..), _) |
(_, ReLateBound(..)) |
(ReEarlyBound(..), _) |
(_, ReEarlyBound(..)) => {
self.tcx.sess.bug(
&format!("cannot relate bound region: GLB({:?}, {:?})",
a,
b));
}
(ReStatic, r) | (r, ReStatic) => {
// static lives longer than everything else
Ok(r)
}
(ReEmpty, _) | (_, ReEmpty) => {
// nothing lives shorter than everything else
Ok(ReEmpty)
}
(ReVar(v_id), _) |
(_, ReVar(v_id)) => {
self.tcx.sess.span_bug(
(*self.var_origins.borrow())[v_id.index as usize].span(),
&format!("glb_concrete_regions invoked with \
non-concrete regions: {:?}, {:?}",
a,
b));
}
(ReFree(fr), ReScope(s_id)) |
(ReScope(s_id), ReFree(fr)) => {
let s = ReScope(s_id);
// Free region is something "at least as big as
// `fr.scope_id`." If we find that the scope `fr.scope_id` is bigger
// than the scope `s_id`, then we can say that the GLB
// is the scope `s_id`. Otherwise, as we do not know
// big the free region is precisely, the GLB is undefined.
if self.tcx.region_maps.nearest_common_ancestor(fr.scope, s_id) == fr.scope ||
free_regions.is_static(fr) {
Ok(s)
} else {
Err(TypeError::RegionsNoOverlap(b, a))
}
}
(ReScope(a_id), ReScope(b_id)) => {
self.intersect_scopes(a, b, a_id, b_id)
}
(ReFree(ref a_fr), ReFree(ref b_fr)) => {
self.glb_free_regions(free_regions, a_fr, b_fr)
}
// For these types, we cannot define any additional
// relationship:
(ReSkolemized(..), _) |
(_, ReSkolemized(..)) => {
if a == b {
Ok(a)
} else {
Err(TypeError::RegionsNoOverlap(b, a))
}
}
}
}
/// Computes a region that is enclosed by both free region arguments, if any. Guarantees that
/// if the same two regions are given as argument, in any order, a consistent result is
/// returned.
fn glb_free_regions(&self,
free_regions: &FreeRegionMap,
a: &FreeRegion,
b: &FreeRegion)
-> RelateResult<'tcx, ty::Region>
{
return match a.cmp(b) {
Less => helper(self, free_regions, a, b),
Greater => helper(self, free_regions, b, a),
Equal => Ok(ty::ReFree(*a))
};
fn helper<'a, 'tcx>(this: &RegionVarBindings<'a, 'tcx>,
free_regions: &FreeRegionMap,
a: &FreeRegion,
b: &FreeRegion) -> RelateResult<'tcx, ty::Region>
{
if free_regions.sub_free_region(*a, *b) {
Ok(ty::ReFree(*a))
} else if free_regions.sub_free_region(*b, *a) {
Ok(ty::ReFree(*b))
} else {
this.intersect_scopes(ty::ReFree(*a), ty::ReFree(*b),
a.scope, b.scope)
}
}
}
fn intersect_scopes(&self,
region_a: ty::Region,
region_b: ty::Region,
scope_a: region::CodeExtent,
scope_b: region::CodeExtent)
-> RelateResult<'tcx, Region>
{
// We want to generate the intersection of two
// scopes or two free regions. So, if one of
// these scopes is a subscope of the other, return
// it. Otherwise fail.
debug!("intersect_scopes(scope_a={:?}, scope_b={:?}, region_a={:?}, region_b={:?})",
scope_a, scope_b, region_a, region_b);
let r_id = self.tcx.region_maps.nearest_common_ancestor(scope_a, scope_b);
if r_id == scope_a {
Ok(ReScope(scope_b))
} else if r_id == scope_b {
Ok(ReScope(scope_a))
} else {
Err(TypeError::RegionsNoOverlap(region_a, region_b))
}
}
}
// ______________________________________________________________________
#[derive(Copy, Clone, PartialEq, Debug)]
enum Classification { Expanding, Contracting }
#[derive(Copy, Clone, Debug)]
pub enum VarValue { NoValue, Value(Region), ErrorValue }
pub enum VarValue { Value(Region), ErrorValue }
struct VarData {
classification: Classification,
value: VarValue,
}
@ -1005,12 +869,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
fn construct_var_data(&self) -> Vec<VarData> {
(0..self.num_vars() as usize).map(|_| {
VarData {
// All nodes are initially classified as contracting; during
// the expansion phase, we will shift the classification for
// those nodes that have a concrete region predecessor to
// Expanding.
classification: Contracting,
value: NoValue,
value: Value(ty::ReEmpty),
}
}).collect()
}
@ -1062,11 +921,11 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
ConstrainVarSubVar(a_vid, b_vid) => {
match var_data[a_vid.index as usize].value {
NoValue | ErrorValue => false,
Value(a_region) => {
let b_node = &mut var_data[b_vid.index as usize];
self.expand_node(free_regions, a_region, b_vid, b_node)
}
ErrorValue => false,
Value(a_region) => {
let b_node = &mut var_data[b_vid.index as usize];
self.expand_node(free_regions, a_region, b_vid, b_node)
}
}
}
ConstrainVarSubReg(..) => {
@ -1100,16 +959,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
_ => { }
}
b_data.classification = Expanding;
match b_data.value {
NoValue => {
debug!("Setting initial value of {:?} to {:?}",
b_vid, a_region);
b_data.value = Value(a_region);
return true;
}
Value(cur_region) => {
let lub = self.lub_concrete_regions(free_regions, a_region, cur_region);
if lub == cur_region {
@ -1148,7 +998,7 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
ConstrainVarSubVar(a_vid, b_vid) => {
match var_data[b_vid.index as usize].value {
NoValue | ErrorValue => false,
ErrorValue => false,
Value(b_region) => {
let a_data = &mut var_data[a_vid.index as usize];
self.contract_node(free_regions, a_vid, a_data, b_region)
@ -1169,27 +1019,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
a_data: &mut VarData,
b_region: Region)
-> bool {
debug!("contract_node({:?} == {:?}/{:?}, {:?})",
a_vid, a_data.value,
a_data.classification, b_region);
debug!("contract_node({:?} == {:?}, {:?})",
a_vid, a_data.value, b_region);
return match a_data.value {
NoValue => {
assert_eq!(a_data.classification, Contracting);
a_data.value = Value(b_region);
true // changed
}
ErrorValue => false, // no change
Value(a_region) => {
match a_data.classification {
Expanding =>
check_node(self, free_regions, a_vid, a_data, a_region, b_region),
Contracting =>
adjust_node(self, free_regions, a_vid, a_data, a_region, b_region),
}
}
Value(a_region) => check_node(self, free_regions, a_vid, a_data, a_region, b_region),
};
fn check_node(this: &RegionVarBindings,
@ -1209,37 +1044,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
}
false
}
fn adjust_node(this: &RegionVarBindings,
free_regions: &FreeRegionMap,
a_vid: RegionVid,
a_data: &mut VarData,
a_region: Region,
b_region: Region)
-> bool {
match this.glb_concrete_regions(free_regions, a_region, b_region) {
Ok(glb) => {
if glb == a_region {
false
} else {
debug!("Contracting value of {:?} from {:?} to {:?}",
a_vid,
a_region,
glb);
a_data.value = Value(glb);
true
}
}
Err(_) => {
debug!("Setting {:?} to ErrorValue: no glb of {:?}, {:?}",
a_vid,
a_region,
b_region);
a_data.value = ErrorValue;
false
}
}
}
}
fn collect_concrete_region_errors(&self,
@ -1308,12 +1112,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
Value(_) => {
/* Inference successful */
}
NoValue => {
/* Unconstrained inference: do not report an error
until the value of this variable is requested.
After all, sometimes we make region variables but never
really use their values. */
}
ErrorValue => {
/* Inference impossible, this value contains
inconsistent constraints.
@ -1339,18 +1137,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
this portion of the code and think hard about it. =) */
let node_vid = RegionVid { index: idx as u32 };
match var_data[idx].classification {
Expanding => {
self.collect_error_for_expanding_node(
free_regions, graph, var_data, &mut dup_vec,
node_vid, errors);
}
Contracting => {
self.collect_error_for_contracting_node(
free_regions, graph, var_data, &mut dup_vec,
node_vid, errors);
}
}
self.collect_error_for_expanding_node(
free_regions, graph, &mut dup_vec, node_vid, errors);
}
}
}
@ -1396,7 +1184,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
fn collect_error_for_expanding_node(&self,
free_regions: &FreeRegionMap,
graph: &RegionGraph,
var_data: &[VarData],
dup_vec: &mut [u32],
node_idx: RegionVid,
errors: &mut Vec<RegionResolutionError<'tcx>>)
@ -1404,11 +1191,9 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
// Errors in expanding nodes result from a lower-bound that is
// not contained by an upper-bound.
let (mut lower_bounds, lower_dup) =
self.collect_concrete_regions(graph, var_data, node_idx,
graph::INCOMING, dup_vec);
self.collect_concrete_regions(graph, node_idx, graph::INCOMING, dup_vec);
let (mut upper_bounds, upper_dup) =
self.collect_concrete_regions(graph, var_data, node_idx,
graph::OUTGOING, dup_vec);
self.collect_concrete_regions(graph, node_idx, graph::OUTGOING, dup_vec);
if lower_dup || upper_dup {
return;
@ -1459,59 +1244,8 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
upper_bounds));
}
fn collect_error_for_contracting_node(
&self,
free_regions: &FreeRegionMap,
graph: &RegionGraph,
var_data: &[VarData],
dup_vec: &mut [u32],
node_idx: RegionVid,
errors: &mut Vec<RegionResolutionError<'tcx>>)
{
// Errors in contracting nodes result from two upper-bounds
// that have no intersection.
let (upper_bounds, dup_found) =
self.collect_concrete_regions(graph, var_data, node_idx,
graph::OUTGOING, dup_vec);
if dup_found {
return;
}
for upper_bound_1 in &upper_bounds {
for upper_bound_2 in &upper_bounds {
match self.glb_concrete_regions(free_regions,
upper_bound_1.region,
upper_bound_2.region) {
Ok(_) => {}
Err(_) => {
let origin = (*self.var_origins.borrow())[node_idx.index as usize].clone();
debug!("region inference error at {:?} for {:?}: \
SupSupConflict sub: {:?} sup: {:?}",
origin, node_idx, upper_bound_1.region, upper_bound_2.region);
errors.push(SupSupConflict(
origin,
upper_bound_1.origin.clone(),
upper_bound_1.region,
upper_bound_2.origin.clone(),
upper_bound_2.region));
return;
}
}
}
}
self.tcx.sess.span_bug(
(*self.var_origins.borrow())[node_idx.index as usize].span(),
&format!("collect_error_for_contracting_node() could not find error \
for var {:?}, upper_bounds={:?}",
node_idx,
upper_bounds));
}
fn collect_concrete_regions(&self,
graph: &RegionGraph,
var_data: &[VarData],
orig_node_idx: RegionVid,
dir: Direction,
dup_vec: &mut [u32])
@ -1536,7 +1270,6 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
while !state.stack.is_empty() {
let node_idx = state.stack.pop().unwrap();
let classification = var_data[node_idx.index as usize].classification;
// check whether we've visited this node on some previous walk
if dup_vec[node_idx.index as usize] == u32::MAX {
@ -1545,17 +1278,12 @@ impl<'a, 'tcx> RegionVarBindings<'a, 'tcx> {
state.dup_found = true;
}
debug!("collect_concrete_regions(orig_node_idx={:?}, node_idx={:?}, \
classification={:?})",
orig_node_idx, node_idx, classification);
debug!("collect_concrete_regions(orig_node_idx={:?}, node_idx={:?})",
orig_node_idx, node_idx);
// figure out the direction from which this node takes its
// values, and search for concrete regions etc in that direction
let dir = match classification {
Expanding => graph::INCOMING,
Contracting => graph::OUTGOING,
};
let dir = graph::INCOMING;
process_edges(self, &mut state, graph, node_idx, dir);
}
@ -1638,7 +1366,6 @@ fn normalize(values: &Vec<VarValue>, r: ty::Region) -> ty::Region {
fn lookup(values: &Vec<VarValue>, rid: ty::RegionVid) -> ty::Region {
match values[rid.index as usize] {
Value(r) => r,
NoValue => ReEmpty, // No constraints, return ty::ReEmpty
ErrorValue => ReStatic, // Previously reported error.
}
}

View File

@ -351,6 +351,11 @@ impl<'a, 'tcx> Env<'a, 'tcx> {
self.tcx().types.isize)
}
pub fn t_rptr_empty(&self) -> Ty<'tcx> {
self.infcx.tcx.mk_imm_ref(self.infcx.tcx.mk_region(ty::ReEmpty),
self.tcx().types.isize)
}
pub fn dummy_type_trace(&self) -> infer::TypeTrace<'tcx> {
infer::TypeTrace::dummy(self.tcx())
}
@ -593,16 +598,15 @@ fn lub_free_free() {
#[test]
fn lub_returning_scope() {
test_env(EMPTY_SOURCE_STR,
errors(&["cannot infer an appropriate lifetime"]), |env| {
env.create_simple_region_hierarchy();
let t_rptr_scope10 = env.t_rptr_scope(10);
let t_rptr_scope11 = env.t_rptr_scope(11);
// this should generate an error when regions are resolved
env.make_lub_ty(env.t_fn(&[], t_rptr_scope10),
env.t_fn(&[], t_rptr_scope11));
})
test_env(EMPTY_SOURCE_STR, errors(&[]), |env| {
env.create_simple_region_hierarchy();
let t_rptr_scope10 = env.t_rptr_scope(10);
let t_rptr_scope11 = env.t_rptr_scope(11);
let t_rptr_empty = env.t_rptr_empty();
env.check_lub(env.t_fn(&[t_rptr_scope10], env.tcx().types.isize),
env.t_fn(&[t_rptr_scope11], env.tcx().types.isize),
env.t_fn(&[t_rptr_empty], env.tcx().types.isize));
});
}
#[test]

View File

@ -259,17 +259,30 @@ pub fn check_pat<'a, 'tcx>(pcx: &pat_ctxt<'a, 'tcx>,
}
}
hir::PatRegion(ref inner, mutbl) => {
let inner_ty = fcx.infcx().next_ty_var();
let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl };
let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span));
let rptr_ty = tcx.mk_ref(tcx.mk_region(region), mt);
let expected = fcx.infcx().shallow_resolve(expected);
if check_dereferencable(pcx, pat.span, expected, &**inner) {
// `demand::subtype` would be good enough, but using
// `eqtype` turns out to be equally general. See (*)
// below for details.
demand::eqtype(fcx, pat.span, expected, rptr_ty);
// Take region, inner-type from expected type if we
// can, to avoid creating needless variables. This
// also helps with the bad interactions of the given
// hack detailed in (*) below.
let (rptr_ty, inner_ty) = match expected.sty {
ty::TyRef(_, mt) if mt.mutbl == mutbl => {
(expected, mt.ty)
}
_ => {
let inner_ty = fcx.infcx().next_ty_var();
let mt = ty::TypeAndMut { ty: inner_ty, mutbl: mutbl };
let region = fcx.infcx().next_region_var(infer::PatternRegion(pat.span));
let rptr_ty = tcx.mk_ref(tcx.mk_region(region), mt);
demand::eqtype(fcx, pat.span, expected, rptr_ty);
(rptr_ty, inner_ty)
}
};
fcx.write_ty(pat.id, rptr_ty);
check_pat(pcx, &**inner, inner_ty);
} else {

View File

@ -1182,9 +1182,10 @@ fn link_fn_args(rcx: &Rcx, body_scope: CodeExtent, args: &[hir::Arg]) {
let arg_ty = rcx.fcx.node_ty(arg.id);
let re_scope = ty::ReScope(body_scope);
let arg_cmt = mc.cat_rvalue(arg.id, arg.ty.span, re_scope, arg_ty);
debug!("arg_ty={:?} arg_cmt={:?}",
debug!("arg_ty={:?} arg_cmt={:?} arg={:?}",
arg_ty,
arg_cmt);
arg_cmt,
arg);
link_pattern(rcx, mc, arg_cmt, &*arg.pat);
}
}
@ -1527,9 +1528,10 @@ pub fn type_must_outlive<'a, 'tcx>(rcx: &Rcx<'a, 'tcx>,
{
let ty = rcx.resolve_type(ty);
debug!("type_must_outlive(ty={:?}, region={:?})",
debug!("type_must_outlive(ty={:?}, region={:?}, origin={:?})",
ty,
region);
region,
origin);
assert!(!ty.has_escaping_regions());