From 771fdd99852ba8465b76674466b470a1ee3dd3c2 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Wed, 8 Apr 2020 11:23:30 +0000 Subject: [PATCH] enforce that R1: R2 requires univ(R1) <= univ(R2) --- .../borrow_check/region_infer/mod.rs | 84 +++++++++++++++++-- .../impl-fn-ignore-binder-via-bottom.rs | 36 ++++++++ .../impl-fn-ignore-binder-via-bottom.stderr | 20 +++++ .../ui/nll/type-check-pointer-comparisons.rs | 8 +- .../nll/type-check-pointer-comparisons.stderr | 14 +++- 5 files changed, 152 insertions(+), 10 deletions(-) create mode 100644 src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs create mode 100644 src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr diff --git a/src/librustc_mir/borrow_check/region_infer/mod.rs b/src/librustc_mir/borrow_check/region_infer/mod.rs index 40012116633..9533d61b7e8 100644 --- a/src/librustc_mir/borrow_check/region_infer/mod.rs +++ b/src/librustc_mir/borrow_check/region_infer/mod.rs @@ -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) diff --git a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs new file mode 100644 index 00000000000..d3964a7f515 --- /dev/null +++ b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.rs @@ -0,0 +1,36 @@ +// Test that the NLL solver cannot find a solution +// for `exists { forall { 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 Y for fn(T) { + type F = fn(T); + + fn make_f() -> Self::F { + |_| {} + } +} + +fn main() { + let _x = ::make_f(); + //~^ higher-ranked subtype error + //~| higher-ranked subtype error + //~| higher-ranked subtype error +} diff --git a/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr new file mode 100644 index 00000000000..70fb877d716 --- /dev/null +++ b/src/test/ui/nll/relate_tys/impl-fn-ignore-binder-via-bottom.stderr @@ -0,0 +1,20 @@ +error: higher-ranked subtype error + --> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14 + | +LL | let _x = ::make_f(); + | ^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14 + | +LL | let _x = ::make_f(); + | ^^^^^^^^^^^^^^^^^^^ + +error: higher-ranked subtype error + --> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14 + | +LL | let _x = ::make_f(); + | ^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 3 previous errors + diff --git a/src/test/ui/nll/type-check-pointer-comparisons.rs b/src/test/ui/nll/type-check-pointer-comparisons.rs index 3c900356fab..298a6ef7ab3 100644 --- a/src/test/ui/nll/type-check-pointer-comparisons.rs +++ b/src/test/ui/nll/type-check-pointer-comparisons.rs @@ -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() {} diff --git a/src/test/ui/nll/type-check-pointer-comparisons.stderr b/src/test/ui/nll/type-check-pointer-comparisons.stderr index f350b861eb6..0fc7480260f 100644 --- a/src/test/ui/nll/type-check-pointer-comparisons.stderr +++ b/src/test/ui/nll/type-check-pointer-comparisons.stderr @@ -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