Skip to content

Commit 771fdd9

Browse files
committed
enforce that R1: R2 requires univ(R1) <= univ(R2)
1 parent 534a41a commit 771fdd9

File tree

5 files changed

+152
-10
lines changed

5 files changed

+152
-10
lines changed

src/librustc_mir/borrow_check/region_infer/mod.rs

Lines changed: 79 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -321,10 +321,75 @@ impl<'tcx> RegionInferenceContext<'tcx> {
321321
let num_sccs = constraints_scc.num_sccs();
322322
let mut scc_universes = IndexVec::from_elem_n(ty::UniverseIndex::MAX, num_sccs);
323323

324+
debug!("compute_scc_universes()");
325+
326+
// For each region R in universe U, ensure that the universe for the SCC
327+
// that contains R is "no bigger" than U. This effectively sets the universe
328+
// for each SCC to be the minimum of the regions within.
324329
for (region_vid, region_definition) in definitions.iter_enumerated() {
325330
let scc = constraints_scc.scc(region_vid);
326331
let scc_universe = &mut scc_universes[scc];
327-
*scc_universe = ::std::cmp::min(*scc_universe, region_definition.universe);
332+
let scc_min = std::cmp::min(region_definition.universe, *scc_universe);
333+
if scc_min != *scc_universe {
334+
*scc_universe = scc_min;
335+
debug!(
336+
"compute_scc_universes: lowered universe of {scc:?} to {scc_min:?} \
337+
because it contains {region_vid:?} in {region_universe:?}",
338+
scc = scc,
339+
scc_min = scc_min,
340+
region_vid = region_vid,
341+
region_universe = region_definition.universe,
342+
);
343+
}
344+
}
345+
346+
// Walk each SCC `A` and `B` such that `A: B`
347+
// and ensure that universe(A) can see universe(B).
348+
//
349+
// This serves to enforce the 'empty/placeholder' hierarchy
350+
// (described in more detail on `RegionKind`):
351+
//
352+
// ```
353+
// static -----+
354+
// | |
355+
// empty(U0) placeholder(U1)
356+
// | /
357+
// empty(U1)
358+
// ```
359+
//
360+
// In particular, imagine we have variables R0 in U0 and R1
361+
// created in U1, and constraints like this;
362+
//
363+
// ```
364+
// R1: !1 // R1 outlives the placeholder in U1
365+
// R1: R0 // R1 outlives R0
366+
// ```
367+
//
368+
// Here, we wish for R1 to be `'static`, because it
369+
// cannot outlive `placeholder(U1)` and `empty(U0)` any other way.
370+
//
371+
// Thanks to this loop, what happens is that the `R1: R0`
372+
// constraint lowers the universe of `R1` to `U0`, which in turn
373+
// means that the `R1: !1` constraint will (later) cause
374+
// `R1` to become `'static`.
375+
for scc_a in constraints_scc.all_sccs() {
376+
for &scc_b in constraints_scc.successors(scc_a) {
377+
let scc_universe_a = scc_universes[scc_a];
378+
let scc_universe_b = scc_universes[scc_b];
379+
let scc_universe_min = std::cmp::min(scc_universe_a, scc_universe_b);
380+
if scc_universe_a != scc_universe_min {
381+
scc_universes[scc_a] = scc_universe_min;
382+
383+
debug!(
384+
"compute_scc_universes: lowered universe of {scc_a:?} to {scc_universe_min:?} \
385+
because {scc_a:?}: {scc_b:?} and {scc_b:?} is in universe {scc_universe_b:?}",
386+
scc_a = scc_a,
387+
scc_b = scc_b,
388+
scc_universe_min = scc_universe_min,
389+
scc_universe_b = scc_universe_b
390+
);
391+
}
392+
}
328393
}
329394

330395
debug!("compute_scc_universes: scc_universe = {:#?}", scc_universes);
@@ -1773,6 +1838,12 @@ impl<'tcx> RegionInferenceContext<'tcx> {
17731838
/// Finds some region R such that `fr1: R` and `R` is live at `elem`.
17741839
crate fn find_sub_region_live_at(&self, fr1: RegionVid, elem: Location) -> RegionVid {
17751840
debug!("find_sub_region_live_at(fr1={:?}, elem={:?})", fr1, elem);
1841+
debug!("find_sub_region_live_at: {:?} is in scc {:?}", fr1, self.constraint_sccs.scc(fr1));
1842+
debug!(
1843+
"find_sub_region_live_at: {:?} is in universe {:?}",
1844+
fr1,
1845+
self.scc_universes[self.constraint_sccs.scc(fr1)]
1846+
);
17761847
self.find_constraint_paths_between_regions(fr1, |r| {
17771848
// First look for some `r` such that `fr1: r` and `r` is live at `elem`
17781849
debug!(
@@ -1794,13 +1865,16 @@ impl<'tcx> RegionInferenceContext<'tcx> {
17941865
.or_else(|| {
17951866
// If we fail to find THAT, it may be that `fr1` is a
17961867
// placeholder that cannot "fit" into its SCC. In that
1797-
// case, there should be some `r` where `fr1: r`, both
1798-
// `fr1` and `r` are in the same SCC, and `fr1` is a
1868+
// case, there should be some `r` where `fr1: r` and `fr1` is a
17991869
// placeholder that `r` cannot name. We can blame that
18001870
// edge.
1871+
//
1872+
// Remember that if `R1: R2`, then the universe of R1
1873+
// must be able to name the universe of R2, because R2 will
1874+
// be at least `'empty(Universe(R2))`, and `R1` must be at
1875+
// larger than that.
18011876
self.find_constraint_paths_between_regions(fr1, |r| {
1802-
self.constraint_sccs.scc(fr1) == self.constraint_sccs.scc(r)
1803-
&& self.cannot_name_placeholder(r, fr1)
1877+
self.cannot_name_placeholder(r, fr1)
18041878
})
18051879
})
18061880
.map(|(_path, r)| r)
Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
// Test that the NLL solver cannot find a solution
2+
// for `exists<R1> { forall<R1> { R2: R1 } }`.
3+
//
4+
// In this test, the impl should match `fn(T)` for some `T`,
5+
// but we ask it to match `for<'a> fn(&'a ())`. Due to argument
6+
// contravariance, this effectively requires a `T = &'b ()` where
7+
// `forall<'a> { 'a: 'b }`. Therefore, we get an error.
8+
//
9+
// Note the use of `-Zno-leak-check` and `feature(nll)` here. These
10+
// are presently required in order to skip the leak-check errors.
11+
//
12+
// c.f. Issue #57642.
13+
//
14+
// compile-flags:-Zno-leak-check
15+
16+
#![feature(nll)]
17+
18+
trait Y {
19+
type F;
20+
fn make_f() -> Self::F;
21+
}
22+
23+
impl<T> Y for fn(T) {
24+
type F = fn(T);
25+
26+
fn make_f() -> Self::F {
27+
|_| {}
28+
}
29+
}
30+
31+
fn main() {
32+
let _x = <fn(&())>::make_f();
33+
//~^ higher-ranked subtype error
34+
//~| higher-ranked subtype error
35+
//~| higher-ranked subtype error
36+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
error: higher-ranked subtype error
2+
--> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14
3+
|
4+
LL | let _x = <fn(&())>::make_f();
5+
| ^^^^^^^^^^^^^^^^^^^
6+
7+
error: higher-ranked subtype error
8+
--> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14
9+
|
10+
LL | let _x = <fn(&())>::make_f();
11+
| ^^^^^^^^^^^^^^^^^^^
12+
13+
error: higher-ranked subtype error
14+
--> $DIR/impl-fn-ignore-binder-via-bottom.rs:32:14
15+
|
16+
LL | let _x = <fn(&())>::make_f();
17+
| ^^^^^^^^^^^^^^^^^^^
18+
19+
error: aborting due to 3 previous errors
20+

src/test/ui/nll/type-check-pointer-comparisons.rs

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,13 +21,13 @@ fn compare_fn_ptr<'a, 'b, 'c>(f: fn(&'c mut &'a i32), g: fn(&'c mut &'b i32)) {
2121
}
2222

2323
fn compare_hr_fn_ptr<'a>(f: fn(&'a i32), g: fn(&i32)) {
24-
// Ideally this should compile with the operands swapped as well, but HIR
25-
// type checking prevents it (and stops compilation) for now.
26-
f == g; // OK
24+
f == g;
25+
//~^ ERROR higher-ranked subtype error
2726
}
2827

2928
fn compare_const_fn_ptr<'a>(f: *const fn(&'a i32), g: *const fn(&i32)) {
30-
f == g; // OK
29+
f == g;
30+
//~^ ERROR higher-ranked subtype error
3131
}
3232

3333
fn main() {}

src/test/ui/nll/type-check-pointer-comparisons.stderr

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -76,5 +76,17 @@ LL | f == g;
7676

7777
help: `'a` and `'b` must be the same: replace one with the other
7878

79-
error: aborting due to 6 previous errors
79+
error: higher-ranked subtype error
80+
--> $DIR/type-check-pointer-comparisons.rs:24:5
81+
|
82+
LL | f == g;
83+
| ^^^^^^
84+
85+
error: higher-ranked subtype error
86+
--> $DIR/type-check-pointer-comparisons.rs:29:5
87+
|
88+
LL | f == g;
89+
| ^^^^^^
90+
91+
error: aborting due to 8 previous errors
8092

0 commit comments

Comments
 (0)