diff options
| author | Santiago Pastorino <spastorino@gmail.com> | 2023-12-06 17:56:07 -0300 |
|---|---|---|
| committer | Santiago Pastorino <spastorino@gmail.com> | 2024-02-29 15:27:59 -0300 |
| commit | 0479287a38ccef11c84f694a9105de8120f6a6b4 (patch) | |
| tree | c708cba63669fe939c214fb0973053aa26e22ec5 | |
| parent | 23ae3dbb31d75b8f7b6793a92b25c9bd96cb5cd9 (diff) | |
| download | rust-0479287a38ccef11c84f694a9105de8120f6a6b4.tar.gz rust-0479287a38ccef11c84f694a9105de8120f6a6b4.zip | |
Make nll higher ranked equate use bidirectional subtyping in invariant context
7 files changed, 162 insertions, 61 deletions
diff --git a/compiler/rustc_borrowck/src/type_check/relate_tys.rs b/compiler/rustc_borrowck/src/type_check/relate_tys.rs index 00296aa38da..85f63371659 100644 --- a/compiler/rustc_borrowck/src/type_check/relate_tys.rs +++ b/compiler/rustc_borrowck/src/type_check/relate_tys.rs @@ -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). - - // 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) - })?; - - self.ambient_variance = variance; - } + 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). + + // 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) + })?; + } - 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. - - // 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); - - self.enter_forall(a, |this, a| { - let b = this.instantiate_binder_with_existentials(b); - this.relate(a, b) - })?; - - self.ambient_variance = variance; + 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. + + // 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) + })?; + } + + 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. + + // 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) + })?; + } + + ty::Variance::Bivariant => {} } Ok(a) diff --git a/tests/ui/closure-expected-type/expect-fn-supply-fn.rs b/tests/ui/closure-expected-type/expect-fn-supply-fn.rs index 7f1c140279c..7599d078351 100644 --- a/tests/ui/closure-expected-type/expect-fn-supply-fn.rs +++ b/tests/ui/closure-expected-type/expect-fn-supply-fn.rs @@ -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() { diff --git a/tests/ui/closure-expected-type/expect-fn-supply-fn.stderr b/tests/ui/closure-expected-type/expect-fn-supply-fn.stderr index e010f0502f8..4215cd0bc11 100644 --- a/tests/ui/closure-expected-type/expect-fn-supply-fn.stderr +++ b/tests/ui/closure-expected-type/expect-fn-supply-fn.stderr @@ -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`. diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs new file mode 100644 index 00000000000..38abf59d844 --- /dev/null +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs @@ -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(); +} diff --git a/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr new file mode 100644 index 00000000000..8c0d66bb0bc --- /dev/null +++ b/tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr @@ -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`. diff --git a/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs b/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs index 039ca1fb58d..52b983cd12f 100644 --- a/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs +++ b/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs @@ -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() {} diff --git a/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr b/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr new file mode 100644 index 00000000000..9a88ce8756c --- /dev/null +++ b/tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr @@ -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`. |
