From c7b6e1de661419a67a59ad59fba70a451c27cbb6 Mon Sep 17 00:00:00 2001 From: lcnr 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 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 ...