Make infer higher ranked equate use bidirectional subtyping in invariant context

This commit is contained in:
Santiago Pastorino 2023-11-27 22:36:39 -03:00
parent 384d26fc7e
commit 23ae3dbb31
No known key found for this signature in database
GPG key ID: 8131A24E0C79EFAF
15 changed files with 153 additions and 47 deletions

View file

@ -168,6 +168,7 @@ pub fn check_intrinsic_type(
let name_str = intrinsic_name.as_str(); let name_str = intrinsic_name.as_str();
let bound_vars = tcx.mk_bound_variable_kinds(&[ let bound_vars = tcx.mk_bound_variable_kinds(&[
ty::BoundVariableKind::Region(ty::BrAnon),
ty::BoundVariableKind::Region(ty::BrAnon), ty::BoundVariableKind::Region(ty::BrAnon),
ty::BoundVariableKind::Region(ty::BrEnv), ty::BoundVariableKind::Region(ty::BrEnv),
]); ]);
@ -181,7 +182,7 @@ pub fn check_intrinsic_type(
let env_region = ty::Region::new_bound( let env_region = ty::Region::new_bound(
tcx, tcx,
ty::INNERMOST, 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()]); 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) (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 => { sym::raw_eq => {
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon }; 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)); 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)), sym::black_box => (1, 0, vec![param(0)], param(0)),

View file

@ -1,5 +1,6 @@
use super::combine::{CombineFields, ObligationEmittingRelation}; use super::combine::{CombineFields, ObligationEmittingRelation};
use super::StructurallyRelateAliases; use super::StructurallyRelateAliases;
use crate::infer::BoundRegionConversionTime::HigherRankedType;
use crate::infer::{DefineOpaqueTypes, SubregionOrigin}; use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
use crate::traits::PredicateObligations; 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() { 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)?; // When equating binders, we check that there is a 1-to-1
self.fields.higher_ranked_sub(b, a, self.a_is_expected)?; // 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 { } else {
// Fast path for the common case. // Fast path for the common case.
self.relate(a.skip_binder(), b.skip_binder())?; self.relate(a.skip_binder(), b.skip_binder())?;

View file

@ -8,7 +8,9 @@ impl<'a> Foo<fn(&'a ())> {
} }
fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {} fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::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 //~| ERROR higher-ranked subtype error
fn main() {} fn main() {}

View file

@ -1,3 +1,22 @@
error[E0308]: mismatched types
--> $DIR/issue-111404-1.rs:10:11
|
LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected struct `Foo<fn(&())>`
found struct `Foo<for<'b> fn(&'b ())>`
error[E0308]: mismatched types
--> $DIR/issue-111404-1.rs:10:11
|
LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
|
= note: expected struct `Foo<fn(&())>`
found struct `Foo<for<'b> fn(&'b ())>`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
error: higher-ranked subtype error error: higher-ranked subtype error
--> $DIR/issue-111404-1.rs:10:1 --> $DIR/issue-111404-1.rs:10:1
| |
@ -12,5 +31,6 @@ LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
| |
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no` = 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`.

View file

@ -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))` // * `for<'r> fn(fn(&'r u32))`
// * `fn(fn(&'a u32)` where `'a` is free // * `fn(fn(&'a u32)` where `'a` is free
// //
// This is because, for `'a = 'static`, the two types overlap. // These were considered equal as for `'a = 'static` subtyping succeeds in both
// Effectively for them to be equal to you get: // directions:
// //
// * `for<'r> fn(fn(&'r u32)) <: fn(fn(&'static u32))` // * `for<'r> fn(fn(&'r u32)) <: fn(fn(&'static u32))`
// * true if `exists<'r> { 'r: 'static }` (obviously true) // * true if `exists<'r> { 'r: 'static }` (obviously true)
@ -15,12 +20,7 @@ trait Trait {}
impl Trait for for<'r> fn(fn(&'r ())) {} impl Trait for for<'r> fn(fn(&'r ())) {}
impl<'a> Trait for fn(fn(&'a ())) {} impl<'a> Trait for fn(fn(&'a ())) {}
//~^ ERROR conflicting implementations //~^ 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
// 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
fn main() {} fn main() {}

View file

@ -1,13 +1,15 @@
error[E0119]: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))` warning: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))`
--> $DIR/coherence-fn-covariant-bound-vs-static.rs:17:1 --> $DIR/coherence-fn-covariant-bound-vs-static.rs:22:1
| |
LL | impl Trait for for<'r> fn(fn(&'r ())) {} LL | impl Trait for for<'r> fn(fn(&'r ())) {}
| ------------------------------------- first implementation here | ------------------------------------- first implementation here
LL | impl<'a> Trait for fn(fn(&'a ())) {} LL | impl<'a> Trait for fn(fn(&'a ())) {}
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'r> fn(fn(&'r ()))` | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'r> fn(fn(&'r ()))`
| |
= warning: the behavior may change in a future release
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details = 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`.

View file

@ -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<'a, 'b> fn(&'a u32, &'b u32)`
// * `for<'c> fn(&'c u32, &'c u32)` // * `for<'c> fn(&'c u32, &'c u32)`
// //
// For a long time we considered these to be distinct types. But in fact they // These types are subtypes of each other as:
// are equivalent, if you work through the implications of subtyping -- this is
// because:
// //
// * `'c` can be the intersection of `'a` and `'b` (and there is always an intersection) // * `'c` can be the intersection of `'a` and `'b` (and there is always an intersection)
// * `'a` and `'b` can both be equal to `'c` // * `'a` and `'b` can both be equal to `'c`
@ -13,7 +14,8 @@
trait Trait {} trait Trait {}
impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {} impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {}
impl Trait for for<'c> fn(&'c u32, &'c 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 // 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 // here. This is because the new leak-check proposed in [MCP 295] does not

View file

@ -1,13 +1,15 @@
error[E0119]: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)` warning: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)`
--> $DIR/coherence-fn-inputs.rs:15:1 --> $DIR/coherence-fn-inputs.rs:16:1
| |
LL | impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {} LL | impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {}
| ----------------------------------------------- first implementation here | ----------------------------------------------- first implementation here
LL | impl Trait for for<'c> fn(&'c u32, &'c u32) { LL | impl Trait for for<'c> fn(&'c u32, &'c u32) {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u32, &'b 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 <https://github.com/rust-lang/rust/issues/56105>
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details = 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`.

View file

@ -7,5 +7,21 @@ LL | WHAT_A_TYPE => 0,
= note: the traits must be derived, manual `impl`s are not sufficient = 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 = 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<T>` is not satisfied
--> $DIR/typeid-equality-by-subtyping.rs:44:51
|
LL | fn unsound<T>(x: <One as WithAssoc<T>>::Assoc) -> <Two as WithAssoc<T>>::Assoc
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WithAssoc<T>` is not implemented for `for<'a, 'b> fn(&'a (), &'b ())`
error[E0277]: the trait bound `for<'a, 'b> fn(&'a (), &'b ()): WithAssoc<T>` is not satisfied
--> $DIR/typeid-equality-by-subtyping.rs:47:1
|
LL | / {
LL | | let x: <Two as WithAssoc<T>>::Assoc = generic::<One, T>(x);
LL | | x
LL | | }
| |_^ the trait `WithAssoc<T>` 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`.

View file

@ -2,8 +2,6 @@
// //
// In particular, we test this pattern in trait solving, where it is not connected // In particular, we test this pattern in trait solving, where it is not connected
// to any part of the source code. // to any part of the source code.
//
//@ check-pass
trait Trait<T> {} trait Trait<T> {}
@ -21,17 +19,17 @@ fn main() {
// - The impl provides the clause `forall<'a> { (): Trait<fn(fn(&'a u32))> }` // - The impl provides the clause `forall<'a> { (): Trait<fn(fn(&'a u32))> }`
// - We instantiate `'a` existentially to get `(): Trait<fn(fn(&?a u32))>` // - We instantiate `'a` existentially to get `(): Trait<fn(fn(&?a u32))>`
// - We unify `fn(fn(&?a u32))` with `for<'b> fn(fn(&'b u32))` -- this does a // - We unify `fn(fn(&?a u32))` with `for<'b> fn(fn(&'b u32))` -- this does a
// "bidirectional" subtyping check, so we wind up with: // "bidirectional" equality check, so we wind up with:
// - `fn(fn(&?a u32)) <: for<'b> fn(fn(&'b u32))` :- // - `fn(fn(&?a u32)) == for<'b> fn(fn(&'b u32))` :-
// - `fn(&!b u32) <: fn(&?a u32)` // - `fn(&!b u32) == fn(&?a u32)`
// - `&?a u32 <: &!b u32` // - `&?a u32 == &!b u32`
// - `?a: !'b` -- solveable if `?a` is inferred to `'static` // - `?a == !b` -- error.
// - `for<'b> fn(fn(&'b u32)) <: fn(fn(&?a u32))` :- // - `fn(fn(&?a u32)) == for<'b> fn(fn(&'b u32))` :-
// - `fn(&?a u32) <: fn(&?b u32)` // - `fn(&?b u32) == fn(&?a u32)`
// - `&?b u32 <: &?a u32` // - `&?a u32 == &?b u32`
// - `?b: ?a` -- solveable if `?b` is inferred to `'static` // - `?a == ?b` -- OK.
// - So the subtyping check succeeds, somewhat surprisingly. // - So the unification fails.
// This is because we can use `'static`.
foo::<()>(); foo::<()>();
//~^ ERROR implementation of `Trait` is not general enough
} }

View file

@ -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<for<'b> fn(fn(&'b u32))>`
= note: ...but it actually implements `Trait<fn(fn(&'0 u32))>`, for some specific lifetime `'0`
error: aborting due to 1 previous error

View file

@ -3,8 +3,6 @@
// error. However, now that we handle subtyping correctly, we no // error. However, now that we handle subtyping correctly, we no
// longer get an error, because we recognize these two types as // longer get an error, because we recognize these two types as
// equivalent! // equivalent!
//
//@ check-pass
fn foo(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) { fn foo(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) {
// The two types above are actually equivalent. With the older // 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 { let z = match 22 {
0 => x, 0 => x,
_ => y, _ => y,
//~^ ERROR `match` arms have incompatible types [E0308]
}; };
} }

View file

@ -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`.

View file

@ -1,5 +1,4 @@
//@ compile-flags: -Znext-solver //@ compile-flags: -Znext-solver
//@ check-pass
trait Trait { trait Trait {
type Ty; 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. // argument is necessary to create universes before registering the hidden type.
fn test<'a>(_: <fn(&u8, &u8) as Trait>::Ty) -> impl Sized { fn test<'a>(_: <fn(&u8, &u8) as Trait>::Ty) -> impl Sized {
//~^ ERROR the type `<for<'a, 'b> fn(&'a u8, &'b u8) as Trait>::Ty` is not well-formed
"hidden type is `&'?0 str` with '?0 member of ['static,]" "hidden type is `&'?0 str` with '?0 member of ['static,]"
} }

View file

@ -0,0 +1,8 @@
error: the type `<for<'a, 'b> 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>(_: <fn(&u8, &u8) as Trait>::Ty) -> impl Sized {
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
error: aborting due to 1 previous error