From 23ae3dbb31d75b8f7b6793a92b25c9bd96cb5cd9 Mon Sep 17 00:00:00 2001 From: Santiago Pastorino Date: Mon, 27 Nov 2023 22:36:39 -0300 Subject: [PATCH] Make infer higher ranked equate use bidirectional subtyping in invariant context --- .../rustc_hir_analysis/src/check/intrinsic.rs | 10 ++++--- .../rustc_infer/src/infer/relate/equate.rs | 27 +++++++++++++++++-- .../issue-111404-1.rs | 4 ++- .../issue-111404-1.stderr | 22 ++++++++++++++- .../coherence-fn-covariant-bound-vs-static.rs | 20 +++++++------- ...erence-fn-covariant-bound-vs-static.stderr | 10 ++++--- tests/ui/coherence/coherence-fn-inputs.rs | 12 +++++---- tests/ui/coherence/coherence-fn-inputs.stderr | 10 ++++--- .../typeid-equality-by-subtyping.stderr | 18 ++++++++++++- .../hrtb-exists-forall-trait-covariant.rs | 24 ++++++++--------- .../hrtb-exists-forall-trait-covariant.stderr | 11 ++++++++ tests/ui/lub-glb/old-lub-glb-hr-eq.rs | 3 +-- tests/ui/lub-glb/old-lub-glb-hr-eq.stderr | 19 +++++++++++++ .../member-constraints-in-root-universe.rs | 2 +- ...member-constraints-in-root-universe.stderr | 8 ++++++ 15 files changed, 153 insertions(+), 47 deletions(-) create mode 100644 tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.stderr create mode 100644 tests/ui/lub-glb/old-lub-glb-hr-eq.stderr create mode 100644 tests/ui/traits/next-solver/member-constraints-in-root-universe.stderr diff --git a/compiler/rustc_hir_analysis/src/check/intrinsic.rs b/compiler/rustc_hir_analysis/src/check/intrinsic.rs index 3b7a7817279f..6e9b4236e208 100644 --- a/compiler/rustc_hir_analysis/src/check/intrinsic.rs +++ b/compiler/rustc_hir_analysis/src/check/intrinsic.rs @@ -168,6 +168,7 @@ pub fn check_intrinsic_type( let name_str = intrinsic_name.as_str(); let bound_vars = tcx.mk_bound_variable_kinds(&[ + ty::BoundVariableKind::Region(ty::BrAnon), ty::BoundVariableKind::Region(ty::BrAnon), ty::BoundVariableKind::Region(ty::BrEnv), ]); @@ -181,7 +182,7 @@ pub fn check_intrinsic_type( let env_region = ty::Region::new_bound( tcx, ty::INNERMOST, - ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv }, + ty::BoundRegion { var: ty::BoundVar::from_u32(2), kind: ty::BrEnv }, ); let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]); (Ty::new_ref(tcx, env_region, ty::TypeAndMut { ty: va_list_ty, mutbl }), va_list_ty) @@ -493,9 +494,12 @@ pub fn check_intrinsic_type( sym::raw_eq => { let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }; - let param_ty = + let param_ty_lhs = Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0)); - (1, 0, vec![param_ty; 2], tcx.types.bool) + let br = ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrAnon }; + let param_ty_rhs = + Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0)); + (1, 0, vec![param_ty_lhs, param_ty_rhs], tcx.types.bool) } sym::black_box => (1, 0, vec![param(0)], param(0)), diff --git a/compiler/rustc_infer/src/infer/relate/equate.rs b/compiler/rustc_infer/src/infer/relate/equate.rs index 43d6aef3e153..1635695a588b 100644 --- a/compiler/rustc_infer/src/infer/relate/equate.rs +++ b/compiler/rustc_infer/src/infer/relate/equate.rs @@ -1,5 +1,6 @@ use super::combine::{CombineFields, ObligationEmittingRelation}; use super::StructurallyRelateAliases; +use crate::infer::BoundRegionConversionTime::HigherRankedType; use crate::infer::{DefineOpaqueTypes, SubregionOrigin}; use crate::traits::PredicateObligations; @@ -168,8 +169,30 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> { } if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() { - self.fields.higher_ranked_sub(a, b, self.a_is_expected)?; - self.fields.higher_ranked_sub(b, a, self.a_is_expected)?; + // When equating binders, we check that there is a 1-to-1 + // correspondence between the bound vars in both types. + // + // We do so by separately instantiating one of the binders with + // placeholders and the other with inference variables and then + // equating the instantiated types. + // + // We want `for<..> A == for<..> B` -- therefore we want + // `exists<..> A == for<..> B` and `exists<..> B == for<..> A`. + + let span = self.fields.trace.cause.span; + let infcx = self.fields.infcx; + + // Check if `exists<..> A == for<..> B` + infcx.enter_forall(b, |b| { + let a = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, a); + self.relate(a, b) + })?; + + // Check if `exists<..> B == for<..> A`. + infcx.enter_forall(a, |a| { + let b = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, b); + self.relate(a, b) + })?; } else { // Fast path for the common case. self.relate(a.skip_binder(), b.skip_binder())?; diff --git a/tests/ui/associated-inherent-types/issue-111404-1.rs b/tests/ui/associated-inherent-types/issue-111404-1.rs index dd62e59f07d2..3255bf20ebd1 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.rs +++ b/tests/ui/associated-inherent-types/issue-111404-1.rs @@ -8,7 +8,9 @@ impl<'a> Foo { } fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} -//~^ ERROR higher-ranked subtype error +//~^ ERROR mismatched types [E0308] +//~| ERROR mismatched types [E0308] +//~| ERROR higher-ranked subtype error //~| ERROR higher-ranked subtype error fn main() {} diff --git a/tests/ui/associated-inherent-types/issue-111404-1.stderr b/tests/ui/associated-inherent-types/issue-111404-1.stderr index cf4d4a5f19b1..5074c877a8ee 100644 --- a/tests/ui/associated-inherent-types/issue-111404-1.stderr +++ b/tests/ui/associated-inherent-types/issue-111404-1.stderr @@ -1,3 +1,22 @@ +error[E0308]: mismatched types + --> $DIR/issue-111404-1.rs:10:11 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected struct `Foo` + found struct `Foo fn(&'b ())>` + +error[E0308]: mismatched types + --> $DIR/issue-111404-1.rs:10:11 + | +LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other + | + = note: expected struct `Foo` + found struct `Foo fn(&'b ())>` + = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` + error: higher-ranked subtype error --> $DIR/issue-111404-1.rs:10:1 | @@ -12,5 +31,6 @@ LL | fn bar(_: fn(Foo fn(Foo::Assoc)>::Assoc)) {} | = note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` -error: aborting due to 2 previous errors +error: aborting due to 4 previous errors +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/coherence/coherence-fn-covariant-bound-vs-static.rs b/tests/ui/coherence/coherence-fn-covariant-bound-vs-static.rs index 99f805f7f0f6..fb706ba1352e 100644 --- a/tests/ui/coherence/coherence-fn-covariant-bound-vs-static.rs +++ b/tests/ui/coherence/coherence-fn-covariant-bound-vs-static.rs @@ -1,10 +1,15 @@ -// Test that impls for these two types are considered ovelapping: +//@ check-pass + +// These types were previously considered equal as they are subtypes of each other. +// This has been changed in #118247 and we now consider them to be disjoint. +// +// In our test: // // * `for<'r> fn(fn(&'r u32))` // * `fn(fn(&'a u32)` where `'a` is free // -// This is because, for `'a = 'static`, the two types overlap. -// Effectively for them to be equal to you get: +// These were considered equal as for `'a = 'static` subtyping succeeds in both +// directions: // // * `for<'r> fn(fn(&'r u32)) <: fn(fn(&'static u32))` // * true if `exists<'r> { 'r: 'static }` (obviously true) @@ -15,12 +20,7 @@ trait Trait {} impl Trait for for<'r> fn(fn(&'r ())) {} impl<'a> Trait for fn(fn(&'a ())) {} -//~^ ERROR conflicting implementations -// -// Note in particular that we do NOT get a future-compatibility warning -// here. This is because the new leak-check proposed in [MCP 295] does not -// "error" when these two types are equated. -// -// [MCP 295]: https://github.com/rust-lang/compiler-team/issues/295 +//~^ WARN conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))` [coherence_leak_check] +//~| WARN the behavior may change in a future release fn main() {} diff --git a/tests/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr b/tests/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr index 316da26b54d4..01694eaf5d11 100644 --- a/tests/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr +++ b/tests/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr @@ -1,13 +1,15 @@ -error[E0119]: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))` - --> $DIR/coherence-fn-covariant-bound-vs-static.rs:17:1 +warning: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))` + --> $DIR/coherence-fn-covariant-bound-vs-static.rs:22:1 | LL | impl Trait for for<'r> fn(fn(&'r ())) {} | ------------------------------------- first implementation here LL | impl<'a> Trait for fn(fn(&'a ())) {} | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'r> fn(fn(&'r ()))` | + = warning: the behavior may change in a future release + = note: for more information, see issue #56105 = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + = note: `#[warn(coherence_leak_check)]` on by default -error: aborting due to 1 previous error +warning: 1 warning emitted -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/coherence/coherence-fn-inputs.rs b/tests/ui/coherence/coherence-fn-inputs.rs index 3afec5c5459a..a88a846329d2 100644 --- a/tests/ui/coherence/coherence-fn-inputs.rs +++ b/tests/ui/coherence/coherence-fn-inputs.rs @@ -1,11 +1,12 @@ -// Test that we consider these two types completely equal: +//@ check-pass + +// These types were previously considered equal as they are subtypes of each other. +// This has been changed in #118247 and we now consider them to be disjoint. // // * `for<'a, 'b> fn(&'a u32, &'b u32)` // * `for<'c> fn(&'c u32, &'c u32)` // -// For a long time we considered these to be distinct types. But in fact they -// are equivalent, if you work through the implications of subtyping -- this is -// because: +// These types are subtypes of each other as: // // * `'c` can be the intersection of `'a` and `'b` (and there is always an intersection) // * `'a` and `'b` can both be equal to `'c` @@ -13,7 +14,8 @@ trait Trait {} impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {} impl Trait for for<'c> fn(&'c u32, &'c u32) { - //~^ ERROR conflicting implementations + //~^ WARN conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)` [coherence_leak_check] + //~| WARN the behavior may change in a future release // // Note in particular that we do NOT get a future-compatibility warning // here. This is because the new leak-check proposed in [MCP 295] does not diff --git a/tests/ui/coherence/coherence-fn-inputs.stderr b/tests/ui/coherence/coherence-fn-inputs.stderr index 246ec5947b3e..56f3a14833e2 100644 --- a/tests/ui/coherence/coherence-fn-inputs.stderr +++ b/tests/ui/coherence/coherence-fn-inputs.stderr @@ -1,13 +1,15 @@ -error[E0119]: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)` - --> $DIR/coherence-fn-inputs.rs:15:1 +warning: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)` + --> $DIR/coherence-fn-inputs.rs:16:1 | LL | impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {} | ----------------------------------------------- first implementation here LL | impl Trait for for<'c> fn(&'c u32, &'c u32) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u32, &'b u32)` | + = warning: the behavior may change in a future release + = note: for more information, see issue #56105 = note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details + = note: `#[warn(coherence_leak_check)]` on by default -error: aborting due to 1 previous error +warning: 1 warning emitted -For more information about this error, try `rustc --explain E0119`. diff --git a/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr b/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr index 220fcd38b097..26e724c90613 100644 --- a/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr +++ b/tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr @@ -7,5 +7,21 @@ LL | WHAT_A_TYPE => 0, = note: the traits must be derived, manual `impl`s are not sufficient = note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details -error: aborting due to 1 previous error +error[E0277]: the trait bound `for<'a, 'b> fn(&'a (), &'b ()): WithAssoc` is not satisfied + --> $DIR/typeid-equality-by-subtyping.rs:44:51 + | +LL | fn unsound(x: >::Assoc) -> >::Assoc + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WithAssoc` is not implemented for `for<'a, 'b> fn(&'a (), &'b ())` +error[E0277]: the trait bound `for<'a, 'b> fn(&'a (), &'b ()): WithAssoc` is not satisfied + --> $DIR/typeid-equality-by-subtyping.rs:47:1 + | +LL | / { +LL | | let x: >::Assoc = generic::(x); +LL | | x +LL | | } + | |_^ the trait `WithAssoc` is not implemented for `for<'a, 'b> fn(&'a (), &'b ())` + +error: aborting due to 3 previous errors + +For more information about this error, try `rustc --explain E0277`. diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.rs b/tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.rs index 230a0524df4d..805fd7f12328 100644 --- a/tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.rs +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.rs @@ -2,8 +2,6 @@ // // In particular, we test this pattern in trait solving, where it is not connected // to any part of the source code. -// -//@ check-pass trait Trait {} @@ -21,17 +19,17 @@ fn main() { // - The impl provides the clause `forall<'a> { (): Trait }` // - We instantiate `'a` existentially to get `(): Trait` // - We unify `fn(fn(&?a u32))` with `for<'b> fn(fn(&'b u32))` -- this does a - // "bidirectional" subtyping check, so we wind up with: - // - `fn(fn(&?a u32)) <: for<'b> fn(fn(&'b u32))` :- - // - `fn(&!b u32) <: fn(&?a u32)` - // - `&?a u32 <: &!b u32` - // - `?a: !'b` -- solveable if `?a` is inferred to `'static` - // - `for<'b> fn(fn(&'b u32)) <: fn(fn(&?a u32))` :- - // - `fn(&?a u32) <: fn(&?b u32)` - // - `&?b u32 <: &?a u32` - // - `?b: ?a` -- solveable if `?b` is inferred to `'static` - // - So the subtyping check succeeds, somewhat surprisingly. - // This is because we can use `'static`. + // "bidirectional" equality check, so we wind up with: + // - `fn(fn(&?a u32)) == for<'b> fn(fn(&'b u32))` :- + // - `fn(&!b u32) == fn(&?a u32)` + // - `&?a u32 == &!b u32` + // - `?a == !b` -- error. + // - `fn(fn(&?a u32)) == for<'b> fn(fn(&'b u32))` :- + // - `fn(&?b u32) == fn(&?a u32)` + // - `&?a u32 == &?b u32` + // - `?a == ?b` -- OK. + // - So the unification fails. foo::<()>(); + //~^ ERROR implementation of `Trait` is not general enough } diff --git a/tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.stderr b/tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.stderr new file mode 100644 index 000000000000..385de795b18f --- /dev/null +++ b/tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.stderr @@ -0,0 +1,11 @@ +error: implementation of `Trait` is not general enough + --> $DIR/hrtb-exists-forall-trait-covariant.rs:33:5 + | +LL | foo::<()>(); + | ^^^^^^^^^^^ implementation of `Trait` is not general enough + | + = note: `()` must implement `Trait fn(fn(&'b u32))>` + = note: ...but it actually implements `Trait`, for some specific lifetime `'0` + +error: aborting due to 1 previous error + diff --git a/tests/ui/lub-glb/old-lub-glb-hr-eq.rs b/tests/ui/lub-glb/old-lub-glb-hr-eq.rs index 97062ec91af5..df288793608d 100644 --- a/tests/ui/lub-glb/old-lub-glb-hr-eq.rs +++ b/tests/ui/lub-glb/old-lub-glb-hr-eq.rs @@ -3,8 +3,6 @@ // error. However, now that we handle subtyping correctly, we no // longer get an error, because we recognize these two types as // equivalent! -// -//@ check-pass fn foo(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) { // The two types above are actually equivalent. With the older @@ -13,6 +11,7 @@ fn foo(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) { let z = match 22 { 0 => x, _ => y, + //~^ ERROR `match` arms have incompatible types [E0308] }; } diff --git a/tests/ui/lub-glb/old-lub-glb-hr-eq.stderr b/tests/ui/lub-glb/old-lub-glb-hr-eq.stderr new file mode 100644 index 000000000000..ea8e53f24605 --- /dev/null +++ b/tests/ui/lub-glb/old-lub-glb-hr-eq.stderr @@ -0,0 +1,19 @@ +error[E0308]: `match` arms have incompatible types + --> $DIR/old-lub-glb-hr-eq.rs:13:14 + | +LL | let z = match 22 { + | _____________- +LL | | 0 => x, + | | - this is found to be of type `for<'a, 'b> fn(&'a u8, &'b u8)` +LL | | _ => y, + | | ^ one type is more general than the other +LL | | +LL | | }; + | |_____- `match` arms have incompatible types + | + = note: expected fn pointer `for<'a, 'b> fn(&'a _, &'b _)` + found fn pointer `for<'a> fn(&'a _, &'a _)` + +error: aborting due to 1 previous error + +For more information about this error, try `rustc --explain E0308`. diff --git a/tests/ui/traits/next-solver/member-constraints-in-root-universe.rs b/tests/ui/traits/next-solver/member-constraints-in-root-universe.rs index 74164ad2aa2d..a5696fc77961 100644 --- a/tests/ui/traits/next-solver/member-constraints-in-root-universe.rs +++ b/tests/ui/traits/next-solver/member-constraints-in-root-universe.rs @@ -1,5 +1,4 @@ //@ compile-flags: -Znext-solver -//@ check-pass trait Trait { type Ty; @@ -11,6 +10,7 @@ impl Trait for for<'a> fn(&'a u8, &'a u8) { // argument is necessary to create universes before registering the hidden type. fn test<'a>(_: ::Ty) -> impl Sized { + //~^ ERROR the type ` fn(&'a u8, &'b u8) as Trait>::Ty` is not well-formed "hidden type is `&'?0 str` with '?0 member of ['static,]" } diff --git a/tests/ui/traits/next-solver/member-constraints-in-root-universe.stderr b/tests/ui/traits/next-solver/member-constraints-in-root-universe.stderr new file mode 100644 index 000000000000..7a9982f07f6f --- /dev/null +++ b/tests/ui/traits/next-solver/member-constraints-in-root-universe.stderr @@ -0,0 +1,8 @@ +error: the type ` fn(&'a u8, &'b u8) as Trait>::Ty` is not well-formed + --> $DIR/member-constraints-in-root-universe.rs:12:16 + | +LL | fn test<'a>(_: ::Ty) -> impl Sized { + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +error: aborting due to 1 previous error +