Make nll higher ranked equate use bidirectional subtyping in invariant context
This commit is contained in:
parent
23ae3dbb31
commit
0479287a38
@ -483,61 +483,59 @@ fn binders<T>(
|
||||
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)
|
||||
|
@ -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() {
|
||||
|
@ -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`.
|
||||
|
38
tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs
Normal file
38
tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs
Normal 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();
|
||||
}
|
@ -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`.
|
@ -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() {}
|
||||
|
22
tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr
Normal file
22
tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr
Normal 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`.
|
Loading…
Reference in New Issue
Block a user