enforce that R1: R2 requires univ(R1) <= univ(R2)
This commit is contained in:
parent
534a41a329
commit
771fdd9985
@ -321,10 +321,75 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
let num_sccs = constraints_scc.num_sccs();
|
||||
let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs);
|
||||
|
||||
debug!("compute_scc_universes()");
|
||||
|
||||
// For each region R in universe U, ensure that the universe for the SCC
|
||||
// that contains R is "no bigger" than U. This effectively sets the universe
|
||||
// for each SCC to be the minimum of the regions within.
|
||||
for (region_vid, region_definition) in definitions.iter_enumerated() {
|
||||
let scc = constraints_scc.scc(region_vid);
|
||||
let scc_universe = &mut scc_universes[scc];
|
||||
*scc_universe = ::std::cmp::min(*scc_universe, region_definition.universe);
|
||||
let scc_min = std::cmp::min(region_definition.universe, *scc_universe);
|
||||
if scc_min != *scc_universe {
|
||||
*scc_universe = scc_min;
|
||||
debug!(
|
||||
"compute_scc_universes: lowered universe of {scc:?} to {scc_min:?} \
|
||||
because it contains {region_vid:?} in {region_universe:?}",
|
||||
scc = scc,
|
||||
scc_min = scc_min,
|
||||
region_vid = region_vid,
|
||||
region_universe = region_definition.universe,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
// Walk each SCC `A` and `B` such that `A: B`
|
||||
// and ensure that universe(A) can see universe(B).
|
||||
//
|
||||
// This serves to enforce the 'empty/placeholder' hierarchy
|
||||
// (described in more detail on `RegionKind`):
|
||||
//
|
||||
// ```
|
||||
// static -----+
|
||||
// | |
|
||||
// empty(U0) placeholder(U1)
|
||||
// | /
|
||||
// empty(U1)
|
||||
// ```
|
||||
//
|
||||
// In particular, imagine we have variables R0 in U0 and R1
|
||||
// created in U1, and constraints like this;
|
||||
//
|
||||
// ```
|
||||
// R1: !1 // R1 outlives the placeholder in U1
|
||||
// R1: R0 // R1 outlives R0
|
||||
// ```
|
||||
//
|
||||
// Here, we wish for R1 to be `'static`, because it
|
||||
// cannot outlive `placeholder(U1)` and `empty(U0)` any other way.
|
||||
//
|
||||
// Thanks to this loop, what happens is that the `R1: R0`
|
||||
// constraint lowers the universe of `R1` to `U0`, which in turn
|
||||
// means that the `R1: !1` constraint will (later) cause
|
||||
// `R1` to become `'static`.
|
||||
for scc_a in constraints_scc.all_sccs() {
|
||||
for &scc_b in constraints_scc.successors(scc_a) {
|
||||
let scc_universe_a = scc_universes[scc_a];
|
||||
let scc_universe_b = scc_universes[scc_b];
|
||||
let scc_universe_min = std::cmp::min(scc_universe_a, scc_universe_b);
|
||||
if scc_universe_a != scc_universe_min {
|
||||
scc_universes[scc_a] = scc_universe_min;
|
||||
|
||||
debug!(
|
||||
"compute_scc_universes: lowered universe of {scc_a:?} to {scc_universe_min:?} \
|
||||
because {scc_a:?}: {scc_b:?} and {scc_b:?} is in universe {scc_universe_b:?}",
|
||||
scc_a = scc_a,
|
||||
scc_b = scc_b,
|
||||
scc_universe_min = scc_universe_min,
|
||||
scc_universe_b = scc_universe_b
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes);
|
||||
@ -1773,6 +1838,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
/// Finds some region R such that `fr1: R` and `R` is live at `elem`.
|
||||
crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
|
||||
debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
|
||||
debug!("find_sub_region_live_at: {:?} is in scc {:?}", fr1, self.constraint_sccs.scc(fr1));
|
||||
debug!(
|
||||
"find_sub_region_live_at: {:?} is in universe {:?}",
|
||||
fr1,
|
||||
self.scc_universes[self.constraint_sccs.scc(fr1)]
|
||||
);
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
// First look for some `r` such that `fr1: r` and `r` is live at `elem`
|
||||
debug!(
|
||||
@ -1794,13 +1865,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
|
||||
.or_else(|| {
|
||||
// If we fail to find THAT, it may be that `fr1` is a
|
||||
// placeholder that cannot "fit" into its SCC. In that
|
||||
// case, there should be some `r` where `fr1: r`, both
|
||||
// `fr1` and `r` are in the same SCC, and `fr1` is a
|
||||
// case, there should be some `r` where `fr1: r` and `fr1` is a
|
||||
// placeholder that `r` cannot name. We can blame that
|
||||
// edge.
|
||||
//
|
||||
// Remember that if `R1: R2`, then the universe of R1
|
||||
// must be able to name the universe of R2, because R2 will
|
||||
// be at least `'empty(Universe(R2))`, and `R1` must be at
|
||||
// larger than that.
|
||||
self.find_constraint_paths_between_regions(fr1, |r| {
|
||||
self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r)
|
||||
&& self.cannot_name_placeholder(r, fr1)
|
||||
self.cannot_name_placeholder(r, fr1)
|
||||
})
|
||||
})
|
||||
.map(|(_path, r)| r)
|
||||
|
@ -0,0 +1,36 @@
|
||||
// Test that the NLL solver cannot find a solution
|
||||
// for `exists<R1> { forall<R1> { R2: R1 } }`.
|
||||
//
|
||||
// In this test, the impl should match `fn(T)` for some `T`,
|
||||
// but we ask it to match `for<'a> fn(&'a ())`. Due to argument
|
||||
// contravariance, this effectively requires a `T = &'b ()` where
|
||||
// `forall<'a> { 'a: 'b }`. Therefore, we get an error.
|
||||
//
|
||||
// Note the use of `-Zno-leak-check` and `feature(nll)` here. These
|
||||
// are presently required in order to skip the leak-check errors.
|
||||
//
|
||||
// c.f. Issue #57642.
|
||||
//
|
||||
// compile-flags:-Zno-leak-check
|
||||
|
||||
#![feature(nll)]
|
||||
|
||||
trait Y {
|
||||
type F;
|
||||
fn make_f() -> Self::F;
|
||||
}
|
||||
|
||||
impl<T> Y for fn(T) {
|
||||
type F = fn(T);
|
||||
|
||||
fn make_f() -> Self::F {
|
||||
|_| {}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let _x = <fn(&())>::make_f();
|
||||
//~^ higher-ranked subtype error
|
||||
//~| higher-ranked subtype error
|
||||
//~| higher-ranked subtype error
|
||||
}
|
@ -0,0 +1,20 @@
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14
|
||||
|
|
||||
LL | let _x = <fn(&())>::make_f();
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14
|
||||
|
|
||||
LL | let _x = <fn(&())>::make_f();
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14
|
||||
|
|
||||
LL | let _x = <fn(&())>::make_f();
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error: aborting due to 3 previous errors
|
||||
|
@ -21,13 +21,13 @@ fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) {
|
||||
}
|
||||
|
||||
fn compare_hr_fn_ptr<'a>(f: fn(&'a i32), g: fn(&i32)) {
|
||||
// Ideally this should compile with the operands swapped as well, but HIR
|
||||
// type checking prevents it (and stops compilation) for now.
|
||||
f == g; // OK
|
||||
f == g;
|
||||
//~^ ERROR higher-ranked subtype error
|
||||
}
|
||||
|
||||
fn compare_const_fn_ptr<'a>(f: *const fn(&'a i32), g: *const fn(&i32)) {
|
||||
f == g; // OK
|
||||
f == g;
|
||||
//~^ ERROR higher-ranked subtype error
|
||||
}
|
||||
|
||||
fn main() {}
|
||||
|
@ -76,5 +76,17 @@ LL | f == g;
|
||||
|
||||
help: `'a` and `'b` must be the same: replace one with the other
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/type-check-pointer-comparisons.rs:24:5
|
||||
|
|
||||
LL | f == g;
|
||||
| ^^^^^^
|
||||
|
||||
error: higher-ranked subtype error
|
||||
--> $DIR/type-check-pointer-comparisons.rs:29:5
|
||||
|
|
||||
LL | f == g;
|
||||
| ^^^^^^
|
||||
|
||||
error: aborting due to 8 previous errors
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user