about summary refs log tree commit diff
diff options
context:
space:
mode:
authorSantiago Pastorino <spastorino@gmail.com>2023-12-06 17:56:07 -0300
committerSantiago Pastorino <spastorino@gmail.com>2024-02-29 15:27:59 -0300
commit0479287a38ccef11c84f694a9105de8120f6a6b4 (patch)
treec708cba63669fe939c214fb0973053aa26e22ec5
parent23ae3dbb31d75b8f7b6793a92b25c9bd96cb5cd9 (diff)
downloadrust-0479287a38ccef11c84f694a9105de8120f6a6b4.tar.gz
rust-0479287a38ccef11c84f694a9105de8120f6a6b4.zip
Make nll higher ranked equate use bidirectional subtyping in invariant context
-rw-r--r--compiler/rustc_borrowck/src/type_check/relate_tys.rs106
-rw-r--r--tests/ui/closure-expected-type/expect-fn-supply-fn.rs6
-rw-r--r--tests/ui/closure-expected-type/expect-fn-supply-fn.stderr24
-rw-r--r--tests/ui/higher-ranked/higher-ranked-lifetime-equality.rs38
-rw-r--r--tests/ui/higher-ranked/higher-ranked-lifetime-equality.stderr22
-rw-r--r--tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.rs5
-rw-r--r--tests/ui/nll/relate_tys/hr-fn-aau-eq-abu.stderr22
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`.