From c7b6e1de661419a67a59ad59fba70a451c27cbb6 Mon Sep 17 00:00:00 2001
From: lcnr <rust@lcnr.de>
Date: Wed, 8 Jun 2022 12:39:14 +0200
Subject: [PATCH 1/2] lub: don't bail out due to empty binders

---
 compiler/rustc_infer/src/infer/glb.rs        | 20 +++++--
 compiler/rustc_infer/src/infer/lub.rs        | 20 +++++--
 src/test/ui/lub-glb/empty-binders-err.rs     | 61 ++++++++++++++++++++
 src/test/ui/lub-glb/empty-binders-err.stderr | 59 +++++++++++++++++++
 src/test/ui/lub-glb/empty-binders.rs         | 45 +++++++++++++++
 5 files changed, 193 insertions(+), 12 deletions(-)
 create mode 100644 src/test/ui/lub-glb/empty-binders-err.rs
 create mode 100644 src/test/ui/lub-glb/empty-binders-err.stderr
 create mode 100644 src/test/ui/lub-glb/empty-binders.rs

diff --git a/compiler/rustc_infer/src/infer/glb.rs b/compiler/rustc_infer/src/infer/glb.rs
index 0d38b94965afe..1570a08f3ca8b 100644
--- a/compiler/rustc_infer/src/infer/glb.rs
+++ b/compiler/rustc_infer/src/infer/glb.rs
@@ -95,12 +95,20 @@ impl<'tcx> TypeRelation<'tcx> for Glb<'_, '_, 'tcx> {
         T: Relate<'tcx>,
     {
         debug!("binders(a={:?}, b={:?})", a, b);
-
-        // When higher-ranked types are involved, computing the LUB is
-        // very challenging, switch to invariance. This is obviously
-        // overly conservative but works ok in practice.
-        self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
-        Ok(a)
+        if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
+            // When higher-ranked types are involved, computing the GLB is
+            // very challenging, switch to invariance. This is obviously
+            // overly conservative but works ok in practice.
+            self.relate_with_variance(
+                ty::Variance::Invariant,
+                ty::VarianceDiagInfo::default(),
+                a,
+                b,
+            )?;
+            Ok(a)
+        } else {
+            Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
+        }
     }
 }
 
diff --git a/compiler/rustc_infer/src/infer/lub.rs b/compiler/rustc_infer/src/infer/lub.rs
index 498c1e907c4e1..9f96d52c85034 100644
--- a/compiler/rustc_infer/src/infer/lub.rs
+++ b/compiler/rustc_infer/src/infer/lub.rs
@@ -95,12 +95,20 @@ impl<'tcx> TypeRelation<'tcx> for Lub<'_, '_, 'tcx> {
         T: Relate<'tcx>,
     {
         debug!("binders(a={:?}, b={:?})", a, b);
-
-        // When higher-ranked types are involved, computing the LUB is
-        // very challenging, switch to invariance. This is obviously
-        // overly conservative but works ok in practice.
-        self.relate_with_variance(ty::Variance::Invariant, ty::VarianceDiagInfo::default(), a, b)?;
-        Ok(a)
+        if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
+            // When higher-ranked types are involved, computing the LUB is
+            // very challenging, switch to invariance. This is obviously
+            // overly conservative but works ok in practice.
+            self.relate_with_variance(
+                ty::Variance::Invariant,
+                ty::VarianceDiagInfo::default(),
+                a,
+                b,
+            )?;
+            Ok(a)
+        } else {
+            Ok(ty::Binder::dummy(self.relate(a.skip_binder(), b.skip_binder())?))
+        }
     }
 }
 
diff --git a/src/test/ui/lub-glb/empty-binders-err.rs b/src/test/ui/lub-glb/empty-binders-err.rs
new file mode 100644
index 0000000000000..ee28dd7c97d61
--- /dev/null
+++ b/src/test/ui/lub-glb/empty-binders-err.rs
@@ -0,0 +1,61 @@
+fn lt<'a: 'a>() -> &'a () {
+    &()
+}
+
+fn lt_in_fn<'a: 'a>() -> fn(&'a ()) {
+    |_| ()
+}
+
+struct Contra<'a>(fn(&'a ()));
+fn lt_in_contra<'a: 'a>() -> Contra<'a> {
+    Contra(|_| ())
+}
+
+fn covariance<'a, 'b, 'upper, 'lower>(v: bool)
+where
+    'upper: 'a,
+    'upper: 'b,
+    'a: 'lower,
+    'b: 'lower,
+
+{
+    let _: &'upper () = match v {
+        //~^ ERROR lifetime may not live long enough
+        //~| ERROR lifetime may not live long enough
+        true => lt::<'a>(),
+        false => lt::<'b>(),
+    };
+}
+
+fn contra_fn<'a, 'b, 'upper, 'lower>(v: bool)
+where
+    'upper: 'a,
+    'upper: 'b,
+    'a: 'lower,
+    'b: 'lower,
+
+{
+
+    let _: fn(&'lower ()) = match v {
+        //~^ ERROR lifetime may not live long enough
+        true => lt_in_fn::<'a>(),
+        false => lt_in_fn::<'b>(),
+    };
+}
+
+fn contra_struct<'a, 'b, 'upper, 'lower>(v: bool)
+where
+    'upper: 'a,
+    'upper: 'b,
+    'a: 'lower,
+    'b: 'lower,
+
+{
+    let _: Contra<'lower> = match v {
+        //~^ ERROR lifetime may not live long enough
+        true => lt_in_contra::<'a>(),
+        false => lt_in_contra::<'b>(),
+    };
+}
+
+fn main() {}
diff --git a/src/test/ui/lub-glb/empty-binders-err.stderr b/src/test/ui/lub-glb/empty-binders-err.stderr
new file mode 100644
index 0000000000000..0d5de978e4301
--- /dev/null
+++ b/src/test/ui/lub-glb/empty-binders-err.stderr
@@ -0,0 +1,59 @@
+error: lifetime may not live long enough
+  --> $DIR/empty-binders-err.rs:22:12
+   |
+LL | fn covariance<'a, 'b, 'upper, 'lower>(v: bool)
+   |               --      ------ lifetime `'upper` defined here
+   |               |
+   |               lifetime `'a` defined here
+...
+LL |     let _: &'upper () = match v {
+   |            ^^^^^^^^^^ type annotation requires that `'a` must outlive `'upper`
+   |
+   = help: consider adding the following bound: `'a: 'upper`
+
+error: lifetime may not live long enough
+  --> $DIR/empty-binders-err.rs:22:12
+   |
+LL | fn covariance<'a, 'b, 'upper, 'lower>(v: bool)
+   |                   --  ------ lifetime `'upper` defined here
+   |                   |
+   |                   lifetime `'b` defined here
+...
+LL |     let _: &'upper () = match v {
+   |            ^^^^^^^^^^ type annotation requires that `'b` must outlive `'upper`
+   |
+   = help: consider adding the following bound: `'b: 'upper`
+
+help: the following changes may resolve your lifetime errors
+   |
+   = help: add bound `'a: 'upper`
+   = help: add bound `'b: 'upper`
+
+error: lifetime may not live long enough
+  --> $DIR/empty-binders-err.rs:39:12
+   |
+LL | fn contra_fn<'a, 'b, 'upper, 'lower>(v: bool)
+   |              --              ------ lifetime `'lower` defined here
+   |              |
+   |              lifetime `'a` defined here
+...
+LL |     let _: fn(&'lower ()) = match v {
+   |            ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
+   |
+   = help: consider adding the following bound: `'lower: 'a`
+
+error: lifetime may not live long enough
+  --> $DIR/empty-binders-err.rs:54:12
+   |
+LL | fn contra_struct<'a, 'b, 'upper, 'lower>(v: bool)
+   |                  --              ------ lifetime `'lower` defined here
+   |                  |
+   |                  lifetime `'a` defined here
+...
+LL |     let _: Contra<'lower> = match v {
+   |            ^^^^^^^^^^^^^^ type annotation requires that `'lower` must outlive `'a`
+   |
+   = help: consider adding the following bound: `'lower: 'a`
+
+error: aborting due to 4 previous errors
+
diff --git a/src/test/ui/lub-glb/empty-binders.rs b/src/test/ui/lub-glb/empty-binders.rs
new file mode 100644
index 0000000000000..f9d07e79fdabf
--- /dev/null
+++ b/src/test/ui/lub-glb/empty-binders.rs
@@ -0,0 +1,45 @@
+// check-pass
+//
+// Check that computing the lub works even for empty binders.
+fn lt<'a: 'a>() -> &'a () {
+    &()
+}
+
+fn lt_in_fn<'a: 'a>() -> fn(&'a ()) {
+    |_| ()
+}
+
+struct Contra<'a>(fn(&'a ()));
+fn lt_in_contra<'a: 'a>() -> Contra<'a> {
+    Contra(|_| ())
+}
+
+fn ok<'a, 'b, 'upper, 'lower>(v: bool)
+where
+    'upper: 'a,
+    'upper: 'b,
+    'a: 'lower,
+    'b: 'lower,
+
+{
+    let _: &'lower () = match v {
+        true => lt::<'a>(),
+        false => lt::<'b>(),
+    };
+
+    // This errored in the past because LUB and GLB always
+    // bailed out when encountering binders, even if they were
+    // empty.
+    let _: fn(&'upper ()) = match v {
+        true => lt_in_fn::<'a>(),
+        false => lt_in_fn::<'b>(),
+    };
+
+    // This was already accepted, as relate didn't encounter any binders.
+    let _: Contra<'upper> = match v {
+        true => lt_in_contra::<'a>(),
+        false => lt_in_contra::<'b>(),
+    };
+}
+
+fn main() {}

From 0667b00acf939634ff1fd310ed5de2d49c65c6bb Mon Sep 17 00:00:00 2001
From: lcnr <rust@lcnr.de>
Date: Wed, 8 Jun 2022 21:03:52 +0200
Subject: [PATCH 2/2] update tests + add future compat test

---
 .../ui/lub-glb/empty-binder-future-compat.rs  | 22 +++++++++++++++++++
 src/test/ui/lub-glb/empty-binders-err.rs      | 12 +++-------
 src/test/ui/lub-glb/empty-binders-err.stderr  | 20 ++++++++---------
 3 files changed, 35 insertions(+), 19 deletions(-)
 create mode 100644 src/test/ui/lub-glb/empty-binder-future-compat.rs

diff --git a/src/test/ui/lub-glb/empty-binder-future-compat.rs b/src/test/ui/lub-glb/empty-binder-future-compat.rs
new file mode 100644
index 0000000000000..8700a88a36ea0
--- /dev/null
+++ b/src/test/ui/lub-glb/empty-binder-future-compat.rs
@@ -0,0 +1,22 @@
+// check-pass
+fn lt_in_fn_fn<'a: 'a>() -> fn(fn(&'a ())) {
+    |_| ()
+}
+
+
+fn foo<'a, 'b, 'lower>(v: bool)
+where
+    'a: 'lower,
+    'b: 'lower,
+{
+        // if we infer `x` to be higher ranked in the future,
+        // this would cause a type error.
+        let x = match v {
+            true => lt_in_fn_fn::<'a>(),
+            false => lt_in_fn_fn::<'b>(),
+        };
+
+        let _: fn(fn(&'lower())) = x;
+}
+
+fn main() {}
diff --git a/src/test/ui/lub-glb/empty-binders-err.rs b/src/test/ui/lub-glb/empty-binders-err.rs
index ee28dd7c97d61..557480173ee62 100644
--- a/src/test/ui/lub-glb/empty-binders-err.rs
+++ b/src/test/ui/lub-glb/empty-binders-err.rs
@@ -11,12 +11,10 @@ fn lt_in_contra<'a: 'a>() -> Contra<'a> {
     Contra(|_| ())
 }
 
-fn covariance<'a, 'b, 'upper, 'lower>(v: bool)
+fn covariance<'a, 'b, 'upper>(v: bool)
 where
     'upper: 'a,
     'upper: 'b,
-    'a: 'lower,
-    'b: 'lower,
 
 {
     let _: &'upper () = match v {
@@ -27,10 +25,8 @@ where
     };
 }
 
-fn contra_fn<'a, 'b, 'upper, 'lower>(v: bool)
+fn contra_fn<'a, 'b, 'lower>(v: bool)
 where
-    'upper: 'a,
-    'upper: 'b,
     'a: 'lower,
     'b: 'lower,
 
@@ -43,10 +39,8 @@ where
     };
 }
 
-fn contra_struct<'a, 'b, 'upper, 'lower>(v: bool)
+fn contra_struct<'a, 'b, 'lower>(v: bool)
 where
-    'upper: 'a,
-    'upper: 'b,
     'a: 'lower,
     'b: 'lower,
 
diff --git a/src/test/ui/lub-glb/empty-binders-err.stderr b/src/test/ui/lub-glb/empty-binders-err.stderr
index 0d5de978e4301..f86f22d5e40bf 100644
--- a/src/test/ui/lub-glb/empty-binders-err.stderr
+++ b/src/test/ui/lub-glb/empty-binders-err.stderr
@@ -1,7 +1,7 @@
 error: lifetime may not live long enough
-  --> $DIR/empty-binders-err.rs:22:12
+  --> $DIR/empty-binders-err.rs:20:12
    |
-LL | fn covariance<'a, 'b, 'upper, 'lower>(v: bool)
+LL | fn covariance<'a, 'b, 'upper>(v: bool)
    |               --      ------ lifetime `'upper` defined here
    |               |
    |               lifetime `'a` defined here
@@ -12,9 +12,9 @@ LL |     let _: &'upper () = match v {
    = help: consider adding the following bound: `'a: 'upper`
 
 error: lifetime may not live long enough
-  --> $DIR/empty-binders-err.rs:22:12
+  --> $DIR/empty-binders-err.rs:20:12
    |
-LL | fn covariance<'a, 'b, 'upper, 'lower>(v: bool)
+LL | fn covariance<'a, 'b, 'upper>(v: bool)
    |                   --  ------ lifetime `'upper` defined here
    |                   |
    |                   lifetime `'b` defined here
@@ -30,10 +30,10 @@ help: the following changes may resolve your lifetime errors
    = help: add bound `'b: 'upper`
 
 error: lifetime may not live long enough
-  --> $DIR/empty-binders-err.rs:39:12
+  --> $DIR/empty-binders-err.rs:35:12
    |
-LL | fn contra_fn<'a, 'b, 'upper, 'lower>(v: bool)
-   |              --              ------ lifetime `'lower` defined here
+LL | fn contra_fn<'a, 'b, 'lower>(v: bool)
+   |              --      ------ lifetime `'lower` defined here
    |              |
    |              lifetime `'a` defined here
 ...
@@ -43,10 +43,10 @@ LL |     let _: fn(&'lower ()) = match v {
    = help: consider adding the following bound: `'lower: 'a`
 
 error: lifetime may not live long enough
-  --> $DIR/empty-binders-err.rs:54:12
+  --> $DIR/empty-binders-err.rs:48:12
    |
-LL | fn contra_struct<'a, 'b, 'upper, 'lower>(v: bool)
-   |                  --              ------ lifetime `'lower` defined here
+LL | fn contra_struct<'a, 'b, 'lower>(v: bool)
+   |                  --      ------ lifetime `'lower` defined here
    |                  |
    |                  lifetime `'a` defined here
 ...