Skip to content

Commit

Permalink
Rollup merge of rust-lang#97867 - lcnr:lub-binder, r=oli-obk
Browse files Browse the repository at this point in the history
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`
  • Loading branch information
Dylan-DPC authored Jun 21, 2022
2 parents 1c53b2d + 0667b00 commit 27887f0
Show file tree
Hide file tree
Showing 6 changed files with 209 additions and 12 deletions.
20 changes: 14 additions & 6 deletions compiler/rustc_infer/src/infer/glb.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,20 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
T: Relate<'tcx>,
{
debug!("binders(a={:?}, b={:?})", a, b);

// When higher-ranked types are involved, computing the LUB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
Ok(a)
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
// When higher-ranked types are involved, computing the GLB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(
ty::Variance::Invariant,
ty::VarianceDiagInfo::default(),
a,
b,
)?;
Ok(a)
} else {
Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
}
}
}

Expand Down
20 changes: 14 additions & 6 deletions compiler/rustc_infer/src/infer/lub.rs
Original file line number Diff line number Diff line change
Expand Up @@ -95,12 +95,20 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
T: Relate<'tcx>,
{
debug!("binders(a={:?}, b={:?})", a, b);

// When higher-ranked types are involved, computing the LUB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
Ok(a)
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
// When higher-ranked types are involved, computing the LUB is
// very challenging, switch to invariance. This is obviously
// overly conservative but works ok in practice.
self.relate_with_variance(
ty::Variance::Invariant,
ty::VarianceDiagInfo::default(),
a,
b,
)?;
Ok(a)
} else {
Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
}
}
}

Expand Down
22 changes: 22 additions & 0 deletions src/test/ui/lub-glb/empty-binder-future-compat.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// check-pass
fn lt_in_fn_fn<'a: 'a>() -> fn(fn(&'a ())) {
|_| ()
}


fn foo<'a, 'b, 'lower>(v: bool)
where
'a: 'lower,
'b: 'lower,
{
// if we infer `x` to be higher ranked in the future,
// this would cause a type error.
let x = match v {
true => lt_in_fn_fn::<'a>(),
false => lt_in_fn_fn::<'b>(),
};

let _: fn(fn(&'lower())) = x;
}

fn main() {}
55 changes: 55 additions & 0 deletions src/test/ui/lub-glb/empty-binders-err.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
fn lt<'a: 'a>() -> &'a () {
&()
}

fn lt_in_fn<'a: 'a>() -> fn(&'a ()) {
|_| ()
}

struct Contra<'a>(fn(&'a ()));
fn lt_in_contra<'a: 'a>() -> Contra<'a> {
Contra(|_| ())
}

fn covariance<'a, 'b, 'upper>(v: bool)
where
'upper: 'a,
'upper: 'b,

{
let _: &'upper () = match v {
//~^ ERROR lifetime may not live long enough
//~| ERROR lifetime may not live long enough
true => lt::<'a>(),
false => lt::<'b>(),
};
}

fn contra_fn<'a, 'b, 'lower>(v: bool)
where
'a: 'lower,
'b: 'lower,

{

let _: fn(&'lower ()) = match v {
//~^ ERROR lifetime may not live long enough
true => lt_in_fn::<'a>(),
false => lt_in_fn::<'b>(),
};
}

fn contra_struct<'a, 'b, 'lower>(v: bool)
where
'a: 'lower,
'b: 'lower,

{
let _: Contra<'lower> = match v {
//~^ ERROR lifetime may not live long enough
true => lt_in_contra::<'a>(),
false => lt_in_contra::<'b>(),
};
}

fn main() {}
59 changes: 59 additions & 0 deletions src/test/ui/lub-glb/empty-binders-err.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
error: lifetime may not live long enough
--> $DIR/empty-binders-err.rs:20:12
|
LL | fn covariance<'a, 'b, 'upper>(v: bool)
| -- ------ lifetime `'upper` defined here
| |
| lifetime `'a` defined here
...
LL | let _: &'upper () = match v {
| ^^^^^^^^^^ type annotation requires that `'a` must outlive `'upper`
|
= help: consider adding the following bound: `'a: 'upper`

error: lifetime may not live long enough
--> $DIR/empty-binders-err.rs:20:12
|
LL | fn covariance<'a, 'b, 'upper>(v: bool)
| -- ------ lifetime `'upper` defined here
| |
| lifetime `'b` defined here
...
LL | let _: &'upper () = match v {
| ^^^^^^^^^^ type annotation requires that `'b` must outlive `'upper`
|
= help: consider adding the following bound: `'b: 'upper`

help: the following changes may resolve your lifetime errors
|
= help: add bound `'a: 'upper`
= help: add bound `'b: 'upper`

error: lifetime may not live long enough
--> $DIR/empty-binders-err.rs:35:12
|
LL | fn contra_fn<'a, 'b, 'lower>(v: bool)
| -- ------ lifetime `'lower` defined here
| |
| lifetime `'a` defined here
...
LL | let _: fn(&'lower ()) = match v {
| ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
|
= help: consider adding the following bound: `'lower: 'a`

error: lifetime may not live long enough
--> $DIR/empty-binders-err.rs:48:12
|
LL | fn contra_struct<'a, 'b, 'lower>(v: bool)
| -- ------ lifetime `'lower` defined here
| |
| lifetime `'a` defined here
...
LL | let _: Contra<'lower> = match v {
| ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
|
= help: consider adding the following bound: `'lower: 'a`

error: aborting due to 4 previous errors

45 changes: 45 additions & 0 deletions src/test/ui/lub-glb/empty-binders.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
// check-pass
//
// Check that computing the lub works even for empty binders.
fn lt<'a: 'a>() -> &'a () {
&()
}

fn lt_in_fn<'a: 'a>() -> fn(&'a ()) {
|_| ()
}

struct Contra<'a>(fn(&'a ()));
fn lt_in_contra<'a: 'a>() -> Contra<'a> {
Contra(|_| ())
}

fn ok<'a, 'b, 'upper, 'lower>(v: bool)
where
'upper: 'a,
'upper: 'b,
'a: 'lower,
'b: 'lower,

{
let _: &'lower () = match v {
true => lt::<'a>(),
false => lt::<'b>(),
};

// This errored in the past because LUB and GLB always
// bailed out when encountering binders, even if they were
// empty.
let _: fn(&'upper ()) = match v {
true => lt_in_fn::<'a>(),
false => lt_in_fn::<'b>(),
};

// This was already accepted, as relate didn't encounter any binders.
let _: Contra<'upper> = match v {
true => lt_in_contra::<'a>(),
false => lt_in_contra::<'b>(),
};
}

fn main() {}

0 comments on commit 27887f0

Please sign in to comment.