Skip to content

Commit fdeecb2

Browse files
authored
Rollup merge of #97867 - lcnr:lub-binder, r=oli-obk
lub: don't bail out due to empty binders allows for the following to compile. The equivalent code using `struct Wrapper<'upper>(fn(&'upper ());` already compiles on stable. ```rust let _: fn(&'upper ()) = match v { true => lt_in_fn::<'a>(), false => lt_in_fn::<'b>(), }; ``` see https://play.rust-lang.org/?version=stable&mode=debug&edition=2021&gist=7034a677190110941223cafac6632f70 for a complete example r? ```@rust-lang/types```
2 parents dc80ca7 + 0667b00 commit fdeecb2

File tree

6 files changed

+209
-12
lines changed

6 files changed

+209
-12
lines changed

compiler/rustc_infer/src/infer/glb.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,20 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
9595
T: Relate<'tcx>,
9696
{
9797
debug!("binders(a={:?}, b={:?})", a, b);
98-
99-
// When higher-ranked types are involved, computing the LUB is
100-
// very challenging, switch to invariance. This is obviously
101-
// overly conservative but works ok in practice.
102-
self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
103-
Ok(a)
98+
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
99+
// When higher-ranked types are involved, computing the GLB is
100+
// very challenging, switch to invariance. This is obviously
101+
// overly conservative but works ok in practice.
102+
self.relate_with_variance(
103+
ty::Variance::Invariant,
104+
ty::VarianceDiagInfo::default(),
105+
a,
106+
b,
107+
)?;
108+
Ok(a)
109+
} else {
110+
Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
111+
}
104112
}
105113
}
106114

compiler/rustc_infer/src/infer/lub.rs

+14-6
Original file line numberDiff line numberDiff line change
@@ -95,12 +95,20 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
9595
T: Relate<'tcx>,
9696
{
9797
debug!("binders(a={:?}, b={:?})", a, b);
98-
99-
// When higher-ranked types are involved, computing the LUB is
100-
// very challenging, switch to invariance. This is obviously
101-
// overly conservative but works ok in practice.
102-
self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
103-
Ok(a)
98+
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
99+
// When higher-ranked types are involved, computing the LUB is
100+
// very challenging, switch to invariance. This is obviously
101+
// overly conservative but works ok in practice.
102+
self.relate_with_variance(
103+
ty::Variance::Invariant,
104+
ty::VarianceDiagInfo::default(),
105+
a,
106+
b,
107+
)?;
108+
Ok(a)
109+
} else {
110+
Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
111+
}
104112
}
105113
}
106114

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// check-pass
2+
fn lt_in_fn_fn<'a: 'a>() -> fn(fn(&'a ())) {
3+
|_| ()
4+
}
5+
6+
7+
fn foo<'a, 'b, 'lower>(v: bool)
8+
where
9+
'a: 'lower,
10+
'b: 'lower,
11+
{
12+
// if we infer `x` to be higher ranked in the future,
13+
// this would cause a type error.
14+
let x = match v {
15+
true => lt_in_fn_fn::<'a>(),
16+
false => lt_in_fn_fn::<'b>(),
17+
};
18+
19+
let _: fn(fn(&'lower())) = x;
20+
}
21+
22+
fn main() {}
+55
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
fn lt<'a: 'a>() -> &'a () {
2+
&()
3+
}
4+
5+
fn lt_in_fn<'a: 'a>() -> fn(&'a ()) {
6+
|_| ()
7+
}
8+
9+
struct Contra<'a>(fn(&'a ()));
10+
fn lt_in_contra<'a: 'a>() -> Contra<'a> {
11+
Contra(|_| ())
12+
}
13+
14+
fn covariance<'a, 'b, 'upper>(v: bool)
15+
where
16+
'upper: 'a,
17+
'upper: 'b,
18+
19+
{
20+
let _: &'upper () = match v {
21+
//~^ ERROR lifetime may not live long enough
22+
//~| ERROR lifetime may not live long enough
23+
true => lt::<'a>(),
24+
false => lt::<'b>(),
25+
};
26+
}
27+
28+
fn contra_fn<'a, 'b, 'lower>(v: bool)
29+
where
30+
'a: 'lower,
31+
'b: 'lower,
32+
33+
{
34+
35+
let _: fn(&'lower ()) = match v {
36+
//~^ ERROR lifetime may not live long enough
37+
true => lt_in_fn::<'a>(),
38+
false => lt_in_fn::<'b>(),
39+
};
40+
}
41+
42+
fn contra_struct<'a, 'b, 'lower>(v: bool)
43+
where
44+
'a: 'lower,
45+
'b: 'lower,
46+
47+
{
48+
let _: Contra<'lower> = match v {
49+
//~^ ERROR lifetime may not live long enough
50+
true => lt_in_contra::<'a>(),
51+
false => lt_in_contra::<'b>(),
52+
};
53+
}
54+
55+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error: lifetime may not live long enough
2+
--> $DIR/empty-binders-err.rs:20:12
3+
|
4+
LL | fn covariance<'a, 'b, 'upper>(v: bool)
5+
| -- ------ lifetime `'upper` defined here
6+
| |
7+
| lifetime `'a` defined here
8+
...
9+
LL | let _: &'upper () = match v {
10+
| ^^^^^^^^^^ type annotation requires that `'a` must outlive `'upper`
11+
|
12+
= help: consider adding the following bound: `'a: 'upper`
13+
14+
error: lifetime may not live long enough
15+
--> $DIR/empty-binders-err.rs:20:12
16+
|
17+
LL | fn covariance<'a, 'b, 'upper>(v: bool)
18+
| -- ------ lifetime `'upper` defined here
19+
| |
20+
| lifetime `'b` defined here
21+
...
22+
LL | let _: &'upper () = match v {
23+
| ^^^^^^^^^^ type annotation requires that `'b` must outlive `'upper`
24+
|
25+
= help: consider adding the following bound: `'b: 'upper`
26+
27+
help: the following changes may resolve your lifetime errors
28+
|
29+
= help: add bound `'a: 'upper`
30+
= help: add bound `'b: 'upper`
31+
32+
error: lifetime may not live long enough
33+
--> $DIR/empty-binders-err.rs:35:12
34+
|
35+
LL | fn contra_fn<'a, 'b, 'lower>(v: bool)
36+
| -- ------ lifetime `'lower` defined here
37+
| |
38+
| lifetime `'a` defined here
39+
...
40+
LL | let _: fn(&'lower ()) = match v {
41+
| ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
42+
|
43+
= help: consider adding the following bound: `'lower: 'a`
44+
45+
error: lifetime may not live long enough
46+
--> $DIR/empty-binders-err.rs:48:12
47+
|
48+
LL | fn contra_struct<'a, 'b, 'lower>(v: bool)
49+
| -- ------ lifetime `'lower` defined here
50+
| |
51+
| lifetime `'a` defined here
52+
...
53+
LL | let _: Contra<'lower> = match v {
54+
| ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
55+
|
56+
= help: consider adding the following bound: `'lower: 'a`
57+
58+
error: aborting due to 4 previous errors
59+

src/test/ui/lub-glb/empty-binders.rs

+45
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
// check-pass
2+
//
3+
// Check that computing the lub works even for empty binders.
4+
fn lt<'a: 'a>() -> &'a () {
5+
&()
6+
}
7+
8+
fn lt_in_fn<'a: 'a>() -> fn(&'a ()) {
9+
|_| ()
10+
}
11+
12+
struct Contra<'a>(fn(&'a ()));
13+
fn lt_in_contra<'a: 'a>() -> Contra<'a> {
14+
Contra(|_| ())
15+
}
16+
17+
fn ok<'a, 'b, 'upper, 'lower>(v: bool)
18+
where
19+
'upper: 'a,
20+
'upper: 'b,
21+
'a: 'lower,
22+
'b: 'lower,
23+
24+
{
25+
let _: &'lower () = match v {
26+
true => lt::<'a>(),
27+
false => lt::<'b>(),
28+
};
29+
30+
// This errored in the past because LUB and GLB always
31+
// bailed out when encountering binders, even if they were
32+
// empty.
33+
let _: fn(&'upper ()) = match v {
34+
true => lt_in_fn::<'a>(),
35+
false => lt_in_fn::<'b>(),
36+
};
37+
38+
// This was already accepted, as relate didn't encounter any binders.
39+
let _: Contra<'upper> = match v {
40+
true => lt_in_contra::<'a>(),
41+
false => lt_in_contra::<'b>(),
42+
};
43+
}
44+
45+
fn main() {}

0 commit comments

Comments
 (0)