smarter algorithm for finding an equal region

Smarter and simpler!
This commit is contained in:
Ali MJ Al-Nasrawy 2023-03-03 06:25:52 +03:00
parent 09524bfd5a
commit 9d74bff829
4 changed files with 11 additions and 106 deletions

View File

@ -1098,51 +1098,22 @@ impl<'tcx> RegionInferenceContext<'tcx> {
let tcx = infcx.tcx;
let ty = tcx.fold_regions(ty, |r, _depth| {
let region_vid = self.to_region_vid(r);
let r_vid = self.to_region_vid(r);
let r_scc = self.constraint_sccs.scc(r_vid);
// The challenge if this. We have some region variable `r`
// whose value is a set of CFG points and universal
// regions. We want to find if that set is *equivalent* to
// any of the named regions found in the closure.
//
// To do so, we compute the
// `non_local_universal_upper_bound`. This will be a
// non-local, universal region that is greater than `r`.
// However, it might not be *contained* within `r`, so
// then we further check whether this bound is contained
// in `r`. If so, we can say that `r` is equivalent to the
// bound.
//
// Let's work through a few examples. For these, imagine
// that we have 3 non-local regions (I'll denote them as
// `'static`, `'a`, and `'b`, though of course in the code
// they would be represented with indices) where:
//
// - `'static: 'a`
// - `'static: 'b`
//
// First, let's assume that `r` is some existential
// variable with an inferred value `{'a, 'static}` (plus
// some CFG nodes). In this case, the non-local upper
// bound is `'static`, since that outlives `'a`. `'static`
// is also a member of `r` and hence we consider `r`
// equivalent to `'static` (and replace it with
// `'static`).
//
// Now let's consider the inferred value `{'a, 'b}`. This
// means `r` is effectively `'a | 'b`. I'm not sure if
// this can come about, actually, but assuming it did, we
// would get a non-local upper bound of `'static`. Since
// `'static` is not contained in `r`, we would fail to
// find an equivalent.
let upper_bound = self.non_local_universal_upper_bound(region_vid);
if self.region_contains(region_vid, upper_bound) {
tcx.mk_re_var(upper_bound)
} else {
// To do so, we simply check every candidate `u_r` for equality.
self.scc_values
.universal_regions_outlived_by(r_scc)
.filter(|&u_r| !self.universal_regions.is_local_free_region(u_r))
.find(|&u_r| self.eval_equal(u_r, r_vid))
.map(|u_r| tcx.mk_re_var(u_r))
// In the case of a failure, use `ReErased`. We will eventually
// return `None` in this case.
tcx.lifetimes.re_erased
}
.unwrap_or(tcx.lifetimes.re_erased)
});
debug!("try_promote_type_test_subject: folded ty = {:?}", ty);
@ -1155,35 +1126,6 @@ impl<'tcx> RegionInferenceContext<'tcx> {
Some(ClosureOutlivesSubject::Ty(ClosureOutlivesSubjectTy::new(tcx, ty)))
}
/// Given some universal or existential region `r`, finds a
/// non-local, universal region `r+` that outlives `r` at entry to (and
/// exit from) the closure. In the worst case, this will be
/// `'static`.
///
/// This is used for two purposes. First, if we are propagated
/// some requirement `T: r`, we can use this method to enlarge `r`
/// to something we can encode for our creator (which only knows
/// about non-local, universal regions). It is also used when
/// encoding `T` as part of `try_promote_type_test_subject` (see
/// that fn for details).
///
/// This is based on the result `'y` of `universal_upper_bound`,
/// except that it converts further takes the non-local upper
/// bound of `'y`, so that the final result is non-local.
fn non_local_universal_upper_bound(&self, r: RegionVid) -> RegionVid {
debug!("non_local_universal_upper_bound(r={:?}={})", r, self.region_value_str(r));
let lub = self.universal_upper_bound(r);
// Grow further to get smallest universal region known to
// creator.
let non_local_lub = self.universal_region_relations.non_local_upper_bound(lub);
debug!("non_local_universal_upper_bound: non_local_lub={:?}", non_local_lub);
non_local_lub
}
/// Returns a universally quantified region that outlives the
/// value of `r` (`r` may be existentially or universally
/// quantified).

View File

@ -93,31 +93,6 @@ impl UniversalRegionRelations<'_> {
res
}
/// Returns the "postdominating" bound of the set of
/// `non_local_upper_bounds` for the given region.
pub(crate) fn non_local_upper_bound(&self, fr: RegionVid) -> RegionVid {
let upper_bounds = self.non_local_upper_bounds(fr);
// In case we find more than one, reduce to one for
// convenience. This is to prevent us from generating more
// complex constraints, but it will cause spurious errors.
let post_dom = self.inverse_outlives.mutual_immediate_postdominator(upper_bounds);
debug!("non_local_bound: post_dom={:?}", post_dom);
post_dom
.and_then(|post_dom| {
// If the mutual immediate postdom is not local, then
// there is no non-local result we can return.
if !self.universal_regions.is_local_free_region(post_dom) {
Some(post_dom)
} else {
None
}
})
.unwrap_or(self.universal_regions.fr_static)
}
/// Finds a "lower bound" for `fr` that is not local. In other
/// words, returns the largest (*) known region `fr1` that (a) is
/// outlived by `fr` and (b) is not local.

View File

@ -1,5 +1,5 @@
// chek-fail
// known-bug: #108639
// See #108639 for description.
// check-pass
trait Trait {
type Item<'a>: 'a;

View File

@ -1,12 +0,0 @@
error[E0310]: the associated type `<I as Trait>::Item<'_>` may not live long enough
--> $DIR/type-test-subject-non-trivial-region.rs:14:9
|
LL | assert_static(a);
| ^^^^^^^^^^^^^^^^
|
= help: consider adding an explicit lifetime bound `<I as Trait>::Item<'_>: 'static`...
= note: ...so that the type `<I as Trait>::Item<'_>` will meet its required lifetime bounds
error: aborting due to previous error
For more information about this error, try `rustc --explain E0310`.