integrate the sub_free_regions code so we have only one copy of it

This commit is contained in:
Niko Matsakis 2019-10-08 10:56:03 -04:00
parent 03b2fff40e
commit b52414fede
5 changed files with 106 additions and 94 deletions

View file

@ -420,12 +420,34 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
/// True if `a <= b`, but not defined over inference variables.
fn sub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> bool {
let tcx = self.tcx();
let sub_free_regions = |r1, r2| self.region_rels.free_regions.sub_free_regions(tcx, r1, r2);
// Check for the case where we know that `'b: 'static` -- in that case,
// `a <= b` for all `a`.
let b_free_or_static = self.region_rels.free_regions.is_free_or_static(b);
if b_free_or_static && sub_free_regions(tcx.lifetimes.re_static, b) {
return true;
}
// If both a and b are free, consult the declared
// relationships. Note that this can be more precise than the
// `lub` relationship defined below, since sometimes the "lub"
// is actually the `postdom_upper_bound` (see
// `TransitiveRelation` for more details).
let a_free_or_static = self.region_rels.free_regions.is_free_or_static(a);
if a_free_or_static && b_free_or_static {
return sub_free_regions(a, b);
}
// For other cases, leverage the LUB code to find the LUB and
// check if it is equal to b.
self.lub_concrete_regions(a, b) == b
}
/// Returns the smallest region `c` such that `a <= c` and `b <= c`.
fn lub_concrete_regions(&self, a: Region<'tcx>, b: Region<'tcx>) -> Region<'tcx> {
match (a, b) {
let r = match (a, b) {
(&ty::ReClosureBound(..), _)
| (_, &ty::ReClosureBound(..))
| (&ReLateBound(..), _)
@ -509,7 +531,11 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
self.tcx().lifetimes.re_static
}
}
}
};
debug!("lub_concrete_regions({:?}, {:?}) = {:?}", a, b, r);
r
}
/// After expansion is complete, go and check upper bounds (i.e.,
@ -528,7 +554,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
Constraint::RegSubReg(sub, sup) => {
if self.region_rels.is_subregion_of(sub, sup) {
if self.sub_concrete_regions(sub, sup) {
continue;
}
@ -557,7 +583,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
// Do not report these errors immediately:
// instead, set the variable value to error and
// collect them later.
if !self.region_rels.is_subregion_of(a_region, b_region) {
if !self.sub_concrete_regions(a_region, b_region) {
debug!(
"collect_errors: region error at {:?}: \
cannot verify that {:?}={:?} <= {:?}",
@ -754,7 +780,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
};
for upper_bound in &upper_bounds {
if !self.region_rels.is_subregion_of(effective_lower_bound, upper_bound.region) {
if !self.sub_concrete_regions(effective_lower_bound, upper_bound.region) {
let origin = self.var_infos[node_idx].origin;
debug!(
"region inference error at {:?} for {:?}: SubSupConflict sub: {:?} \
@ -884,7 +910,7 @@ impl<'cx, 'tcx> LexicalResolver<'cx, 'tcx> {
}
VerifyBound::OutlivedBy(r) => {
self.region_rels.is_subregion_of(min, var_values.normalize(self.tcx(), r))
self.sub_concrete_regions(min, var_values.normalize(self.tcx(), r))
}
VerifyBound::IsEmpty => {

View file

@ -384,9 +384,9 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
match least_region {
None => least_region = Some(subst_arg),
Some(lr) => {
if free_region_relations.sub_free_regions(lr, subst_arg) {
if free_region_relations.sub_free_regions(self.tcx, lr, subst_arg) {
// keep the current least region
} else if free_region_relations.sub_free_regions(subst_arg, lr) {
} else if free_region_relations.sub_free_regions(self.tcx, subst_arg, lr) {
// switch to `subst_arg`
least_region = Some(subst_arg);
} else {

View file

@ -4,8 +4,8 @@
//! and use that to decide when one free region outlives another, and so forth.
use crate::middle::region;
use crate::ty::free_region_map::{FreeRegionMap, FreeRegionRelations};
use crate::ty::{self, Region, TyCtxt};
use crate::ty::free_region_map::FreeRegionMap;
use crate::ty::{Region, TyCtxt};
use rustc_hir::def_id::DefId;
/// Combines a `region::ScopeTree` (which governs relationships between
@ -38,62 +38,6 @@ impl<'a, 'tcx> RegionRelations<'a, 'tcx> {
Self { tcx, context, region_scope_tree, free_regions }
}
/// Determines whether one region is a subregion of another. This is intended to run *after
/// inference* and sadly the logic is somewhat duplicated with the code in infer.rs.
pub fn is_subregion_of(
&self,
sub_region: ty::Region<'tcx>,
super_region: ty::Region<'tcx>,
) -> bool {
let result = sub_region == super_region || {
match (sub_region, super_region) {
(ty::ReEmpty, _) | (_, ty::ReStatic) => true,
(ty::ReScope(sub_scope), ty::ReScope(super_scope)) => {
self.region_scope_tree.is_subscope_of(*sub_scope, *super_scope)
}
(ty::ReScope(sub_scope), ty::ReEarlyBound(ref 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)
}
(ty::ReScope(sub_scope), ty::ReFree(fr)) => {
let fr_scope = self.region_scope_tree.free_scope(self.tcx, fr);
self.region_scope_tree.is_subscope_of(*sub_scope, fr_scope)
}
(ty::ReEarlyBound(_), ty::ReEarlyBound(_))
| (ty::ReFree(_), ty::ReEarlyBound(_))
| (ty::ReEarlyBound(_), ty::ReFree(_))
| (ty::ReFree(_), ty::ReFree(_)) => {
self.free_regions.sub_free_regions(sub_region, super_region)
}
_ => false,
}
};
let result = result || self.is_static(super_region);
debug!(
"is_subregion_of(sub_region={:?}, super_region={:?}) = {:?}",
sub_region, super_region, result
);
result
}
/// Determines whether this free region is required to be `'static`.
fn is_static(&self, super_region: ty::Region<'tcx>) -> bool {
debug!("is_static(super_region={:?})", super_region);
match *super_region {
ty::ReStatic => true,
ty::ReEarlyBound(_) | ty::ReFree(_) => {
let re_static = self.tcx.mk_region(ty::ReStatic);
self.free_regions.sub_free_regions(&re_static, &super_region)
}
_ => false,
}
}
pub fn lub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> Region<'tcx> {
self.free_regions.lub_free_regions(self.tcx, r_a, r_b)
}

View file

@ -23,11 +23,61 @@ impl<'tcx> FreeRegionMap<'tcx> {
// (with the exception that `'static: 'x` is not notable)
pub fn relate_regions(&mut self, sub: Region<'tcx>, sup: Region<'tcx>) {
debug!("relate_regions(sub={:?}, sup={:?})", sub, sup);
if is_free_or_static(sub) && is_free(sup) {
if self.is_free_or_static(sub) && self.is_free(sup) {
self.relation.add(sub, sup)
}
}
/// Tests whether `r_a <= r_b`.
///
/// Both regions must meet `is_free_or_static`.
///
/// Subtle: one tricky case that this code gets correct is as
/// follows. If we know that `r_b: 'static`, then this function
/// will return true, even though we don't know anything that
/// directly relates `r_a` and `r_b`.
///
/// Also available through the `FreeRegionRelations` trait below.
pub fn sub_free_regions(
&self,
tcx: TyCtxt<'tcx>,
r_a: Region<'tcx>,
r_b: Region<'tcx>,
) -> bool {
assert!(self.is_free_or_static(r_a) && self.is_free_or_static(r_b));
let re_static = tcx.lifetimes.re_static;
if self.check_relation(re_static, r_b) {
// `'a <= 'static` is always true, and not stored in the
// relation explicitly, so check if `'b` is `'static` (or
// equivalent to it)
true
} else {
self.check_relation(r_a, r_b)
}
}
/// Check whether `r_a <= r_b` is found in the relation
fn check_relation(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
r_a == r_b || self.relation.contains(&r_a, &r_b)
}
/// True for free regions other than `'static`.
pub fn is_free(&self, r: Region<'_>) -> bool {
match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) => true,
_ => false,
}
}
/// True if `r` is a free region or static of the sort that this
/// free region map can be used with.
pub fn is_free_or_static(&self, r: Region<'_>) -> bool {
match *r {
ty::ReStatic => true,
_ => self.is_free(r),
}
}
/// Computes the least-upper-bound of two free regions. In some
/// cases, this is more conservative than necessary, in order to
/// avoid making arbitrary choices. See
@ -39,13 +89,13 @@ impl<'tcx> FreeRegionMap<'tcx> {
r_b: Region<'tcx>,
) -> Region<'tcx> {
debug!("lub_free_regions(r_a={:?}, r_b={:?})", r_a, r_b);
assert!(is_free(r_a));
assert!(is_free(r_b));
assert!(self.is_free(r_a));
assert!(self.is_free(r_b));
let result = if r_a == r_b {
r_a
} else {
match self.relation.postdom_upper_bound(&r_a, &r_b) {
None => tcx.mk_region(ty::ReStatic),
None => tcx.lifetimes.re_static,
Some(r) => *r,
}
};
@ -60,31 +110,18 @@ impl<'tcx> FreeRegionMap<'tcx> {
pub trait FreeRegionRelations<'tcx> {
/// Tests whether `r_a <= r_b`. Both must be free regions or
/// `'static`.
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool;
fn sub_free_regions(
&self,
tcx: TyCtxt<'tcx>,
shorter: ty::Region<'tcx>,
longer: ty::Region<'tcx>,
) -> bool;
}
impl<'tcx> FreeRegionRelations<'tcx> for FreeRegionMap<'tcx> {
fn sub_free_regions(&self, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
assert!(is_free_or_static(r_a) && is_free_or_static(r_b));
if let ty::ReStatic = r_b {
true // `'a <= 'static` is just always true, and not stored in the relation explicitly
} else {
r_a == r_b || self.relation.contains(&r_a, &r_b)
}
}
}
fn is_free(r: Region<'_>) -> bool {
match *r {
ty::ReEarlyBound(_) | ty::ReFree(_) => true,
_ => false,
}
}
fn is_free_or_static(r: Region<'_>) -> bool {
match *r {
ty::ReStatic => true,
_ => is_free(r),
fn sub_free_regions(&self, tcx: TyCtxt<'tcx>, r_a: Region<'tcx>, r_b: Region<'tcx>) -> bool {
// invoke the "inherent method"
self.sub_free_regions(tcx, r_a, r_b)
}
}

View file

@ -5,7 +5,7 @@ use rustc::mir::ConstraintCategory;
use rustc::traits::query::outlives_bounds::{self, OutlivesBound};
use rustc::traits::query::type_op::{self, TypeOp};
use rustc::ty::free_region_map::FreeRegionRelations;
use rustc::ty::{self, RegionVid, Ty};
use rustc::ty::{self, RegionVid, Ty, TyCtxt};
use rustc_data_structures::transitive_relation::TransitiveRelation;
use rustc_span::DUMMY_SP;
use std::rc::Rc;
@ -359,7 +359,12 @@ impl UniversalRegionRelationsBuilder<'cx, 'tcx> {
/// over the `FreeRegionMap` from lexical regions and
/// `UniversalRegions` (from NLL)`.
impl<'tcx> FreeRegionRelations<'tcx> for UniversalRegionRelations<'tcx> {
fn sub_free_regions(&self, shorter: ty::Region<'tcx>, longer: ty::Region<'tcx>) -> bool {
fn sub_free_regions(
&self,
_tcx: TyCtxt<'tcx>,
shorter: ty::Region<'tcx>,
longer: ty::Region<'tcx>,
) -> bool {
let shorter = shorter.to_region_vid();
assert!(self.universal_regions.is_universal_region(shorter));
let longer = longer.to_region_vid();