Make nll higher ranked equate use bidirectional subtyping in invariant context

This commit is contained in:
Santiago Pastorino 2023-12-06 17:56:07 -03:00
parent 23ae3dbb31
commit 0479287a38
No known key found for this signature in database
GPG Key ID: 8131A24E0C79EFAF
7 changed files with 157 additions and 56 deletions

View File

@ -483,61 +483,59 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
return Ok(ty::Binder::dummy(a));
}
if self.ambient_covariance() {
// Covariance, so we want `for<..> A <: for<..> B` --
// therefore we compare any instantiation of A (i.e., A
// instantiated with existentials) against every
// instantiation of B (i.e., B instantiated with
// universals).
match self.ambient_variance {
ty::Variance::Covariant => {
// Covariance, so we want `for<..> A <: for<..> B` --
// therefore we compare any instantiation of A (i.e., A
// instantiated with existentials) against every
// instantiation of B (i.e., B instantiated with
// universals).
// Reset the ambient variance to covariant. This is needed
// to correctly handle cases like
//
// for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32)
//
// Somewhat surprisingly, these two types are actually
// **equal**, even though the one on the right looks more
// polymorphic. The reason is due to subtyping. To see it,
// consider that each function can call the other:
//
// - The left function can call the right with `'b` and
// `'c` both equal to `'a`
//
// - The right function can call the left with `'a` set to
// `{P}`, where P is the point in the CFG where the call
// itself occurs. Note that `'b` and `'c` must both
// include P. At the point, the call works because of
// subtyping (i.e., `&'b u32 <: &{P} u32`).
let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant);
// Note: the order here is important. Create the placeholders first, otherwise
// we assign the wrong universe to the existential!
self.enter_forall(b, |this, b| {
let a = this.instantiate_binder_with_existentials(a);
this.relate(a, b)
})?;
}
// Note: the order here is important. Create the placeholders first, otherwise
// we assign the wrong universe to the existential!
self.enter_forall(b, |this, b| {
let a = this.instantiate_binder_with_existentials(a);
this.relate(a, b)
})?;
ty::Variance::Contravariant => {
// Contravariance, so we want `for<..> A :> for<..> B` --
// therefore we compare every instantiation of A (i.e., A
// instantiated with universals) against any
// instantiation of B (i.e., B instantiated with
// existentials). Opposite of above.
self.ambient_variance = variance;
}
// Note: the order here is important. Create the placeholders first, otherwise
// we assign the wrong universe to the existential!
self.enter_forall(a, |this, a| {
let b = this.instantiate_binder_with_existentials(b);
this.relate(a, b)
})?;
}
if self.ambient_contravariance() {
// Contravariance, so we want `for<..> A :> for<..> B`
// -- therefore we compare every instantiation of A (i.e.,
// A instantiated with universals) against any
// instantiation of B (i.e., B instantiated with
// existentials). Opposite of above.
ty::Variance::Invariant => {
// Invariant, so we want `for<..> A == for<..> B` --
// therefore we want `exists<..> A == for<..> B` and
// `exists<..> B == for<..> A`.
//
// See the comment in `fn Equate::binders` for more details.
// Reset ambient variance to contravariance. See the
// covariant case above for an explanation.
let variance =
std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);
// Note: the order here is important. Create the placeholders first, otherwise
// we assign the wrong universe to the existential!
self.enter_forall(b, |this, b| {
let a = this.instantiate_binder_with_existentials(a);
this.relate(a, b)
})?;
// Note: the order here is important. Create the placeholders first, otherwise
// we assign the wrong universe to the existential!
self.enter_forall(a, |this, a| {
let b = this.instantiate_binder_with_existentials(b);
this.relate(a, b)
})?;
}
self.enter_forall(a, |this, a| {
let b = this.instantiate_binder_with_existentials(b);
this.relate(a, b)
})?;
self.ambient_variance = variance;
ty::Variance::Bivariant => {}
}
Ok(a)

View File

@ -30,14 +30,16 @@ fn expect_free_supply_bound() {
// Here, we are given a function whose region is bound at closure level,
// but we expect one bound in the argument. Error results.
with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
//~^ ERROR mismatched types
//~^ ERROR mismatched types [E0308]
//~| ERROR lifetime may not live long enough
}
fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) {
// Here, we are given a `fn(&u32)` but we expect a `fn(&'x
// u32)`. In principle, this could be ok, but we demand equality.
with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
//~^ ERROR mismatched types
//~^ ERROR mismatched types [E0308]
//~| ERROR lifetime may not live long enough
}
fn expect_bound_supply_free_from_closure() {

View File

@ -19,6 +19,15 @@ LL | fn expect_free_supply_free_from_fn<'x>(x: &'x u32) {
LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {});
| ^ requires that `'x` must outlive `'static`
error: lifetime may not live long enough
--> $DIR/expect-fn-supply-fn.rs:32:49
|
LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
| ^
| |
| has type `fn(&'1 u32)`
| requires that `'1` must outlive `'static`
error[E0308]: mismatched types
--> $DIR/expect-fn-supply-fn.rs:32:49
|
@ -29,7 +38,7 @@ LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
found fn pointer `for<'a> fn(&'a _)`
error[E0308]: mismatched types
--> $DIR/expect-fn-supply-fn.rs:39:50
--> $DIR/expect-fn-supply-fn.rs:40:50
|
LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
| ^ one type is more general than the other
@ -37,8 +46,17 @@ LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
= note: expected fn pointer `for<'a> fn(&'a _)`
found fn pointer `fn(&_)`
error: lifetime may not live long enough
--> $DIR/expect-fn-supply-fn.rs:40:50
|
LL | fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) {
| -- lifetime `'x` defined here
...
LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
| ^ requires that `'x` must outlive `'static`
error[E0308]: mismatched types
--> $DIR/expect-fn-supply-fn.rs:48:50
--> $DIR/expect-fn-supply-fn.rs:50:50
|
LL | with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| {
| ^ one type is more general than the other
@ -46,6 +64,6 @@ LL | with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| {
= note: expected fn pointer `for<'a> fn(&'a _)`
found fn pointer `fn(&_)`
error: aborting due to 5 previous errors
error: aborting due to 7 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -0,0 +1,38 @@
// A regression test for #97156
type One = for<'a> fn(&'a (), &'a ());
type Two = for<'a, 'b> fn(&'a (), &'b ());
mod my_api {
use std::any::Any;
use std::marker::PhantomData;
pub struct Foo<T: 'static> {
a: &'static dyn Any,
_p: PhantomData<*mut T>, // invariant, the type of the `dyn Any`
}
impl<T: 'static> Foo<T> {
pub fn deref(&self) -> &'static T {
match self.a.downcast_ref::<T>() {
None => unsafe { std::hint::unreachable_unchecked() },
Some(a) => a,
}
}
pub fn new(a: T) -> Foo<T> {
Foo::<T> { a: Box::leak(Box::new(a)), _p: PhantomData }
}
}
}
use my_api::*;
fn main() {
let foo = Foo::<One>::new((|_, _| ()) as One);
foo.deref();
let foo: Foo<Two> = foo;
//~^ ERROR mismatched types [E0308]
//~| ERROR mismatched types [E0308]
foo.deref();
}

View File

@ -0,0 +1,22 @@
error[E0308]: mismatched types
--> $DIR/higher-ranked-lifetime-equality.rs:34:25
|
LL | let foo: Foo<Two> = foo;
| ^^^ one type is more general than the other
|
= note: expected struct `my_api::Foo<for<'a, 'b> fn(&'a (), &'b ())>`
found struct `my_api::Foo<for<'a> fn(&'a (), &'a ())>`
error[E0308]: mismatched types
--> $DIR/higher-ranked-lifetime-equality.rs:34:25
|
LL | let foo: Foo<Two> = foo;
| ^^^ one type is more general than the other
|
= note: expected struct `my_api::Foo<for<'a, 'b> fn(&'a (), &'b ())>`
found struct `my_api::Foo<for<'a> fn(&'a (), &'a ())>`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.

View File

@ -6,7 +6,6 @@
// another -- effectively, the single lifetime `'a` is just inferred
// to be the intersection of the two distinct lifetimes.
//
//@ check-pass
//@ compile-flags:-Zno-leak-check
use std::cell::Cell;
@ -17,7 +16,9 @@ fn make_cell_aa() -> Cell<for<'a> fn(&'a u32, &'a u32)> {
fn aa_eq_ab() {
let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
//~^ ERROR mismatched types [E0308]
//~| ERROR mismatched types [E0308]
drop(a);
}
fn main() { }
fn main() {}

View File

@ -0,0 +1,22 @@
error[E0308]: mismatched types
--> $DIR/hr-fn-aau-eq-abu.rs:18:53
|
LL | let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
| ^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected struct `Cell<for<'a, 'b> fn(&'a _, &'b _)>`
found struct `Cell<for<'a> fn(&'a _, &'a _)>`
error[E0308]: mismatched types
--> $DIR/hr-fn-aau-eq-abu.rs:18:53
|
LL | let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
| ^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected struct `Cell<for<'a, 'b> fn(&'a _, &'b _)>`
found struct `Cell<for<'a> fn(&'a _, &'a _)>`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: aborting due to 2 previous errors
For more information about this error, try `rustc --explain E0308`.