Skip to content

Commit 0479287

Browse files
committed
Make nll higher ranked equate use bidirectional subtyping in invariant context
1 parent 23ae3db commit 0479287

File tree

7 files changed

+162
-61
lines changed

7 files changed

+162
-61
lines changed

Diff for: compiler/rustc_borrowck/src/type_check/relate_tys.rs

+52-54
Original file line numberDiff line numberDiff line change
@@ -483,61 +483,59 @@ impl<'bccx, 'tcx> TypeRelation<'tcx> for NllTypeRelating<'_, 'bccx, 'tcx> {
483483
return Ok(ty::Binder::dummy(a));
484484
}
485485

486-
if self.ambient_covariance() {
487-
// Covariance, so we want `for<..> A <: for<..> B` --
488-
// therefore we compare any instantiation of A (i.e., A
489-
// instantiated with existentials) against every
490-
// instantiation of B (i.e., B instantiated with
491-
// universals).
492-
493-
// Reset the ambient variance to covariant. This is needed
494-
// to correctly handle cases like
495-
//
496-
// for<'a> fn(&'a u32, &'a u32) == for<'b, 'c> fn(&'b u32, &'c u32)
497-
//
498-
// Somewhat surprisingly, these two types are actually
499-
// **equal**, even though the one on the right looks more
500-
// polymorphic. The reason is due to subtyping. To see it,
501-
// consider that each function can call the other:
502-
//
503-
// - The left function can call the right with `'b` and
504-
// `'c` both equal to `'a`
505-
//
506-
// - The right function can call the left with `'a` set to
507-
// `{P}`, where P is the point in the CFG where the call
508-
// itself occurs. Note that `'b` and `'c` must both
509-
// include P. At the point, the call works because of
510-
// subtyping (i.e., `&'b u32 <: &{P} u32`).
511-
let variance = std::mem::replace(&mut self.ambient_variance, ty::Variance::Covariant);
512-
513-
// Note: the order here is important. Create the placeholders first, otherwise
514-
// we assign the wrong universe to the existential!
515-
self.enter_forall(b, |this, b| {
516-
let a = this.instantiate_binder_with_existentials(a);
517-
this.relate(a, b)
518-
})?;
519-
520-
self.ambient_variance = variance;
521-
}
486+
match self.ambient_variance {
487+
ty::Variance::Covariant => {
488+
// Covariance, so we want `for<..> A <: for<..> B` --
489+
// therefore we compare any instantiation of A (i.e., A
490+
// instantiated with existentials) against every
491+
// instantiation of B (i.e., B instantiated with
492+
// universals).
493+
494+
// Note: the order here is important. Create the placeholders first, otherwise
495+
// we assign the wrong universe to the existential!
496+
self.enter_forall(b, |this, b| {
497+
let a = this.instantiate_binder_with_existentials(a);
498+
this.relate(a, b)
499+
})?;
500+
}
522501

523-
if self.ambient_contravariance() {
524-
// Contravariance, so we want `for<..> A :> for<..> B`
525-
// -- therefore we compare every instantiation of A (i.e.,
526-
// A instantiated with universals) against any
527-
// instantiation of B (i.e., B instantiated with
528-
// existentials). Opposite of above.
529-
530-
// Reset ambient variance to contravariance. See the
531-
// covariant case above for an explanation.
532-
let variance =
533-
std::mem::replace(&mut self.ambient_variance, ty::Variance::Contravariant);
534-
535-
self.enter_forall(a, |this, a| {
536-
let b = this.instantiate_binder_with_existentials(b);
537-
this.relate(a, b)
538-
})?;
539-
540-
self.ambient_variance = variance;
502+
ty::Variance::Contravariant => {
503+
// Contravariance, so we want `for<..> A :> for<..> B` --
504+
// therefore we compare every instantiation of A (i.e., A
505+
// instantiated with universals) against any
506+
// instantiation of B (i.e., B instantiated with
507+
// existentials). Opposite of above.
508+
509+
// Note: the order here is important. Create the placeholders first, otherwise
510+
// we assign the wrong universe to the existential!
511+
self.enter_forall(a, |this, a| {
512+
let b = this.instantiate_binder_with_existentials(b);
513+
this.relate(a, b)
514+
})?;
515+
}
516+
517+
ty::Variance::Invariant => {
518+
// Invariant, so we want `for<..> A == for<..> B` --
519+
// therefore we want `exists<..> A == for<..> B` and
520+
// `exists<..> B == for<..> A`.
521+
//
522+
// See the comment in `fn Equate::binders` for more details.
523+
524+
// Note: the order here is important. Create the placeholders first, otherwise
525+
// we assign the wrong universe to the existential!
526+
self.enter_forall(b, |this, b| {
527+
let a = this.instantiate_binder_with_existentials(a);
528+
this.relate(a, b)
529+
})?;
530+
// Note: the order here is important. Create the placeholders first, otherwise
531+
// we assign the wrong universe to the existential!
532+
self.enter_forall(a, |this, a| {
533+
let b = this.instantiate_binder_with_existentials(b);
534+
this.relate(a, b)
535+
})?;
536+
}
537+
538+
ty::Variance::Bivariant => {}
541539
}
542540

543541
Ok(a)

Diff for: tests/ui/closure-expected-type/expect-fn-supply-fn.rs

+4-2
Original file line numberDiff line numberDiff line change
@@ -30,14 +30,16 @@ fn expect_free_supply_bound() {
3030
// Here, we are given a function whose region is bound at closure level,
3131
// but we expect one bound in the argument. Error results.
3232
with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
33-
//~^ ERROR mismatched types
33+
//~^ ERROR mismatched types [E0308]
34+
//~| ERROR lifetime may not live long enough
3435
}
3536

3637
fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) {
3738
// Here, we are given a `fn(&u32)` but we expect a `fn(&'x
3839
// u32)`. In principle, this could be ok, but we demand equality.
3940
with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
40-
//~^ ERROR mismatched types
41+
//~^ ERROR mismatched types [E0308]
42+
//~| ERROR lifetime may not live long enough
4143
}
4244

4345
fn expect_bound_supply_free_from_closure() {

Diff for: tests/ui/closure-expected-type/expect-fn-supply-fn.stderr

+21-3
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,15 @@ LL | fn expect_free_supply_free_from_fn<'x>(x: &'x u32) {
1919
LL | with_closure_expecting_fn_with_free_region(|x: fn(&'x u32), y| {});
2020
| ^ requires that `'x` must outlive `'static`
2121

22+
error: lifetime may not live long enough
23+
--> $DIR/expect-fn-supply-fn.rs:32:49
24+
|
25+
LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
26+
| ^
27+
| |
28+
| has type `fn(&'1 u32)`
29+
| requires that `'1` must outlive `'static`
30+
2231
error[E0308]: mismatched types
2332
--> $DIR/expect-fn-supply-fn.rs:32:49
2433
|
@@ -29,23 +38,32 @@ LL | with_closure_expecting_fn_with_free_region(|x: fn(&u32), y| {});
2938
found fn pointer `for<'a> fn(&'a _)`
3039

3140
error[E0308]: mismatched types
32-
--> $DIR/expect-fn-supply-fn.rs:39:50
41+
--> $DIR/expect-fn-supply-fn.rs:40:50
3342
|
3443
LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
3544
| ^ one type is more general than the other
3645
|
3746
= note: expected fn pointer `for<'a> fn(&'a _)`
3847
found fn pointer `fn(&_)`
3948

49+
error: lifetime may not live long enough
50+
--> $DIR/expect-fn-supply-fn.rs:40:50
51+
|
52+
LL | fn expect_bound_supply_free_from_fn<'x>(x: &'x u32) {
53+
| -- lifetime `'x` defined here
54+
...
55+
LL | with_closure_expecting_fn_with_bound_region(|x: fn(&'x u32), y| {});
56+
| ^ requires that `'x` must outlive `'static`
57+
4058
error[E0308]: mismatched types
41-
--> $DIR/expect-fn-supply-fn.rs:48:50
59+
--> $DIR/expect-fn-supply-fn.rs:50:50
4260
|
4361
LL | with_closure_expecting_fn_with_bound_region(|x: Foo<'_>, y| {
4462
| ^ one type is more general than the other
4563
|
4664
= note: expected fn pointer `for<'a> fn(&'a _)`
4765
found fn pointer `fn(&_)`
4866

49-
error: aborting due to 5 previous errors
67+
error: aborting due to 7 previous errors
5068

5169
For more information about this error, try `rustc --explain E0308`.
+38
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// A regression test for #97156
2+
3+
type One = for<'a> fn(&'a (), &'a ());
4+
type Two = for<'a, 'b> fn(&'a (), &'b ());
5+
6+
mod my_api {
7+
use std::any::Any;
8+
use std::marker::PhantomData;
9+
10+
pub struct Foo<T: 'static> {
11+
a: &'static dyn Any,
12+
_p: PhantomData<*mut T>, // invariant, the type of the `dyn Any`
13+
}
14+
15+
impl<T: 'static> Foo<T> {
16+
pub fn deref(&self) -> &'static T {
17+
match self.a.downcast_ref::<T>() {
18+
None => unsafe { std::hint::unreachable_unchecked() },
19+
Some(a) => a,
20+
}
21+
}
22+
23+
pub fn new(a: T) -> Foo<T> {
24+
Foo::<T> { a: Box::leak(Box::new(a)), _p: PhantomData }
25+
}
26+
}
27+
}
28+
29+
use my_api::*;
30+
31+
fn main() {
32+
let foo = Foo::<One>::new((|_, _| ()) as One);
33+
foo.deref();
34+
let foo: Foo<Two> = foo;
35+
//~^ ERROR mismatched types [E0308]
36+
//~| ERROR mismatched types [E0308]
37+
foo.deref();
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/higher-ranked-lifetime-equality.rs:34:25
3+
|
4+
LL | let foo: Foo<Two> = foo;
5+
| ^^^ one type is more general than the other
6+
|
7+
= note: expected struct `my_api::Foo<for<'a, 'b> fn(&'a (), &'b ())>`
8+
found struct `my_api::Foo<for<'a> fn(&'a (), &'a ())>`
9+
10+
error[E0308]: mismatched types
11+
--> $DIR/higher-ranked-lifetime-equality.rs:34:25
12+
|
13+
LL | let foo: Foo<Two> = foo;
14+
| ^^^ one type is more general than the other
15+
|
16+
= note: expected struct `my_api::Foo<for<'a, 'b> fn(&'a (), &'b ())>`
17+
found struct `my_api::Foo<for<'a> fn(&'a (), &'a ())>`
18+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
19+
20+
error: aborting due to 2 previous errors
21+
22+
For more information about this error, try `rustc --explain E0308`.

Diff for: tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs

+3-2
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@
66
// another -- effectively, the single lifetime `'a` is just inferred
77
// to be the intersection of the two distinct lifetimes.
88
//
9-
//@ check-pass
109
//@ compile-flags:-Zno-leak-check
1110

1211
use std::cell::Cell;
@@ -17,7 +16,9 @@ fn make_cell_aa() -> Cell<for<'a> fn(&'a u32, &'a u32)> {
1716

1817
fn aa_eq_ab() {
1918
let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
19+
//~^ ERROR mismatched types [E0308]
20+
//~| ERROR mismatched types [E0308]
2021
drop(a);
2122
}
2223

23-
fn main() { }
24+
fn main() {}

Diff for: tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr

+22
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/hr-fn-aau-eq-abu.rs:18:53
3+
|
4+
LL | let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
5+
| ^^^^^^^^^^^^^^ one type is more general than the other
6+
|
7+
= note: expected struct `Cell<for<'a, 'b> fn(&'a _, &'b _)>`
8+
found struct `Cell<for<'a> fn(&'a _, &'a _)>`
9+
10+
error[E0308]: mismatched types
11+
--> $DIR/hr-fn-aau-eq-abu.rs:18:53
12+
|
13+
LL | let a: Cell<for<'a, 'b> fn(&'a u32, &'b u32)> = make_cell_aa();
14+
| ^^^^^^^^^^^^^^ one type is more general than the other
15+
|
16+
= note: expected struct `Cell<for<'a, 'b> fn(&'a _, &'b _)>`
17+
found struct `Cell<for<'a> fn(&'a _, &'a _)>`
18+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
19+
20+
error: aborting due to 2 previous errors
21+
22+
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)