Skip to content

Commit 23ae3db

Browse files
committed
Make infer higher ranked equate use bidirectional subtyping in invariant context
1 parent 384d26f commit 23ae3db

15 files changed

+153
-47
lines changed

compiler/rustc_hir_analysis/src/check/intrinsic.rs

+7-3
Original file line numberDiff line numberDiff line change
@@ -168,6 +168,7 @@ pub fn check_intrinsic_type(
168168
let name_str = intrinsic_name.as_str();
169169

170170
let bound_vars = tcx.mk_bound_variable_kinds(&[
171+
ty::BoundVariableKind::Region(ty::BrAnon),
171172
ty::BoundVariableKind::Region(ty::BrAnon),
172173
ty::BoundVariableKind::Region(ty::BrEnv),
173174
]);
@@ -181,7 +182,7 @@ pub fn check_intrinsic_type(
181182
let env_region = ty::Region::new_bound(
182183
tcx,
183184
ty::INNERMOST,
184-
ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrEnv },
185+
ty::BoundRegion { var: ty::BoundVar::from_u32(2), kind: ty::BrEnv },
185186
);
186187
let va_list_ty = tcx.type_of(did).instantiate(tcx, &[region.into()]);
187188
(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(
493494

494495
sym::raw_eq => {
495496
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(0), kind: ty::BrAnon };
496-
let param_ty =
497+
let param_ty_lhs =
498+
Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
499+
let br = ty::BoundRegion { var: ty::BoundVar::from_u32(1), kind: ty::BrAnon };
500+
let param_ty_rhs =
497501
Ty::new_imm_ref(tcx, ty::Region::new_bound(tcx, ty::INNERMOST, br), param(0));
498-
(1, 0, vec![param_ty; 2], tcx.types.bool)
502+
(1, 0, vec![param_ty_lhs, param_ty_rhs], tcx.types.bool)
499503
}
500504

501505
sym::black_box => (1, 0, vec![param(0)], param(0)),

compiler/rustc_infer/src/infer/relate/equate.rs

+25-2
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use super::combine::{CombineFields, ObligationEmittingRelation};
22
use super::StructurallyRelateAliases;
3+
use crate::infer::BoundRegionConversionTime::HigherRankedType;
34
use crate::infer::{DefineOpaqueTypes, SubregionOrigin};
45
use crate::traits::PredicateObligations;
56

@@ -168,8 +169,30 @@ impl<'tcx> TypeRelation<'tcx> for Equate<'_, '_, 'tcx> {
168169
}
169170

170171
if a.skip_binder().has_escaping_bound_vars() || b.skip_binder().has_escaping_bound_vars() {
171-
self.fields.higher_ranked_sub(a, b, self.a_is_expected)?;
172-
self.fields.higher_ranked_sub(b, a, self.a_is_expected)?;
172+
// When equating binders, we check that there is a 1-to-1
173+
// correspondence between the bound vars in both types.
174+
//
175+
// We do so by separately instantiating one of the binders with
176+
// placeholders and the other with inference variables and then
177+
// equating the instantiated types.
178+
//
179+
// We want `for<..> A == for<..> B` -- therefore we want
180+
// `exists<..> A == for<..> B` and `exists<..> B == for<..> A`.
181+
182+
let span = self.fields.trace.cause.span;
183+
let infcx = self.fields.infcx;
184+
185+
// Check if `exists<..> A == for<..> B`
186+
infcx.enter_forall(b, |b| {
187+
let a = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, a);
188+
self.relate(a, b)
189+
})?;
190+
191+
// Check if `exists<..> B == for<..> A`.
192+
infcx.enter_forall(a, |a| {
193+
let b = infcx.instantiate_binder_with_fresh_vars(span, HigherRankedType, b);
194+
self.relate(a, b)
195+
})?;
173196
} else {
174197
// Fast path for the common case.
175198
self.relate(a.skip_binder(), b.skip_binder())?;

tests/ui/associated-inherent-types/issue-111404-1.rs

+3-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,9 @@ impl<'a> Foo<fn(&'a ())> {
88
}
99

1010
fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
11-
//~^ ERROR higher-ranked subtype error
11+
//~^ ERROR mismatched types [E0308]
12+
//~| ERROR mismatched types [E0308]
13+
//~| ERROR higher-ranked subtype error
1214
//~| ERROR higher-ranked subtype error
1315

1416
fn main() {}

tests/ui/associated-inherent-types/issue-111404-1.stderr

+21-1
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,22 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-111404-1.rs:10:11
3+
|
4+
LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
6+
|
7+
= note: expected struct `Foo<fn(&())>`
8+
found struct `Foo<for<'b> fn(&'b ())>`
9+
10+
error[E0308]: mismatched types
11+
--> $DIR/issue-111404-1.rs:10:11
12+
|
13+
LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ one type is more general than the other
15+
|
16+
= note: expected struct `Foo<fn(&())>`
17+
found struct `Foo<for<'b> fn(&'b ())>`
18+
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
19+
120
error: higher-ranked subtype error
221
--> $DIR/issue-111404-1.rs:10:1
322
|
@@ -12,5 +31,6 @@ LL | fn bar(_: fn(Foo<for<'b> fn(Foo<fn(&'b ())>::Assoc)>::Assoc)) {}
1231
|
1332
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
1433

15-
error: aborting due to 2 previous errors
34+
error: aborting due to 4 previous errors
1635

36+
For more information about this error, try `rustc --explain E0308`.
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,15 @@
1-
// Test that impls for these two types are considered ovelapping:
1+
//@ check-pass
2+
3+
// These types were previously considered equal as they are subtypes of each other.
4+
// This has been changed in #118247 and we now consider them to be disjoint.
5+
//
6+
// In our test:
27
//
38
// * `for<'r> fn(fn(&'r u32))`
49
// * `fn(fn(&'a u32)` where `'a` is free
510
//
6-
// This is because, for `'a = 'static`, the two types overlap.
7-
// Effectively for them to be equal to you get:
11+
// These were considered equal as for `'a = 'static` subtyping succeeds in both
12+
// directions:
813
//
914
// * `for<'r> fn(fn(&'r u32)) <: fn(fn(&'static u32))`
1015
// * true if `exists<'r> { 'r: 'static }` (obviously true)
@@ -15,12 +20,7 @@ trait Trait {}
1520

1621
impl Trait for for<'r> fn(fn(&'r ())) {}
1722
impl<'a> Trait for fn(fn(&'a ())) {}
18-
//~^ ERROR conflicting implementations
19-
//
20-
// Note in particular that we do NOT get a future-compatibility warning
21-
// here. This is because the new leak-check proposed in [MCP 295] does not
22-
// "error" when these two types are equated.
23-
//
24-
// [MCP 295]: https://github.com/rust-lang/compiler-team/issues/295
23+
//~^ WARN conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))` [coherence_leak_check]
24+
//~| WARN the behavior may change in a future release
2525

2626
fn main() {}

tests/ui/coherence/coherence-fn-covariant-bound-vs-static.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
error[E0119]: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))`
2-
--> $DIR/coherence-fn-covariant-bound-vs-static.rs:17:1
1+
warning: conflicting implementations of trait `Trait` for type `for<'r> fn(fn(&'r ()))`
2+
--> $DIR/coherence-fn-covariant-bound-vs-static.rs:22:1
33
|
44
LL | impl Trait for for<'r> fn(fn(&'r ())) {}
55
| ------------------------------------- first implementation here
66
LL | impl<'a> Trait for fn(fn(&'a ())) {}
77
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'r> fn(fn(&'r ()))`
88
|
9+
= warning: the behavior may change in a future release
10+
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
911
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
12+
= note: `#[warn(coherence_leak_check)]` on by default
1013

11-
error: aborting due to 1 previous error
14+
warning: 1 warning emitted
1215

13-
For more information about this error, try `rustc --explain E0119`.

tests/ui/coherence/coherence-fn-inputs.rs

+7-5
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,21 @@
1-
// Test that we consider these two types completely equal:
1+
//@ check-pass
2+
3+
// These types were previously considered equal as they are subtypes of each other.
4+
// This has been changed in #118247 and we now consider them to be disjoint.
25
//
36
// * `for<'a, 'b> fn(&'a u32, &'b u32)`
47
// * `for<'c> fn(&'c u32, &'c u32)`
58
//
6-
// For a long time we considered these to be distinct types. But in fact they
7-
// are equivalent, if you work through the implications of subtyping -- this is
8-
// because:
9+
// These types are subtypes of each other as:
910
//
1011
// * `'c` can be the intersection of `'a` and `'b` (and there is always an intersection)
1112
// * `'a` and `'b` can both be equal to `'c`
1213

1314
trait Trait {}
1415
impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {}
1516
impl Trait for for<'c> fn(&'c u32, &'c u32) {
16-
//~^ ERROR conflicting implementations
17+
//~^ WARN conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)` [coherence_leak_check]
18+
//~| WARN the behavior may change in a future release
1719
//
1820
// Note in particular that we do NOT get a future-compatibility warning
1921
// here. This is because the new leak-check proposed in [MCP 295] does not

tests/ui/coherence/coherence-fn-inputs.stderr

+6-4
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
1-
error[E0119]: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)`
2-
--> $DIR/coherence-fn-inputs.rs:15:1
1+
warning: conflicting implementations of trait `Trait` for type `for<'a, 'b> fn(&'a u32, &'b u32)`
2+
--> $DIR/coherence-fn-inputs.rs:16:1
33
|
44
LL | impl Trait for for<'a, 'b> fn(&'a u32, &'b u32) {}
55
| ----------------------------------------------- first implementation here
66
LL | impl Trait for for<'c> fn(&'c u32, &'c u32) {
77
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ conflicting implementation for `for<'a, 'b> fn(&'a u32, &'b u32)`
88
|
9+
= warning: the behavior may change in a future release
10+
= note: for more information, see issue #56105 <https://github.com/rust-lang/rust/issues/56105>
911
= note: this behavior recently changed as a result of a bug fix; see rust-lang/rust#56105 for details
12+
= note: `#[warn(coherence_leak_check)]` on by default
1013

11-
error: aborting due to 1 previous error
14+
warning: 1 warning emitted
1215

13-
For more information about this error, try `rustc --explain E0119`.

tests/ui/const-generics/generic_const_exprs/typeid-equality-by-subtyping.stderr

+17-1
Original file line numberDiff line numberDiff line change
@@ -7,5 +7,21 @@ LL | WHAT_A_TYPE => 0,
77
= note: the traits must be derived, manual `impl`s are not sufficient
88
= note: see https://doc.rust-lang.org/stable/std/marker/trait.StructuralPartialEq.html for details
99

10-
error: aborting due to 1 previous error
10+
error[E0277]: the trait bound `for<'a, 'b> fn(&'a (), &'b ()): WithAssoc<T>` is not satisfied
11+
--> $DIR/typeid-equality-by-subtyping.rs:44:51
12+
|
13+
LL | fn unsound<T>(x: <One as WithAssoc<T>>::Assoc) -> <Two as WithAssoc<T>>::Assoc
14+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `WithAssoc<T>` is not implemented for `for<'a, 'b> fn(&'a (), &'b ())`
15+
16+
error[E0277]: the trait bound `for<'a, 'b> fn(&'a (), &'b ()): WithAssoc<T>` is not satisfied
17+
--> $DIR/typeid-equality-by-subtyping.rs:47:1
18+
|
19+
LL | / {
20+
LL | | let x: <Two as WithAssoc<T>>::Assoc = generic::<One, T>(x);
21+
LL | | x
22+
LL | | }
23+
| |_^ the trait `WithAssoc<T>` is not implemented for `for<'a, 'b> fn(&'a (), &'b ())`
24+
25+
error: aborting due to 3 previous errors
1126

27+
For more information about this error, try `rustc --explain E0277`.

tests/ui/higher-ranked/trait-bounds/hrtb-exists-forall-trait-covariant.rs

+11-13
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,6 @@
22
//
33
// In particular, we test this pattern in trait solving, where it is not connected
44
// to any part of the source code.
5-
//
6-
//@ check-pass
75

86
trait Trait<T> {}
97

@@ -21,17 +19,17 @@ fn main() {
2119
// - The impl provides the clause `forall<'a> { (): Trait<fn(fn(&'a u32))> }`
2220
// - We instantiate `'a` existentially to get `(): Trait<fn(fn(&?a u32))>`
2321
// - We unify `fn(fn(&?a u32))` with `for<'b> fn(fn(&'b u32))` -- this does a
24-
// "bidirectional" subtyping check, so we wind up with:
25-
// - `fn(fn(&?a u32)) <: for<'b> fn(fn(&'b u32))` :-
26-
// - `fn(&!b u32) <: fn(&?a u32)`
27-
// - `&?a u32 <: &!b u32`
28-
// - `?a: !'b` -- solveable if `?a` is inferred to `'static`
29-
// - `for<'b> fn(fn(&'b u32)) <: fn(fn(&?a u32))` :-
30-
// - `fn(&?a u32) <: fn(&?b u32)`
31-
// - `&?b u32 <: &?a u32`
32-
// - `?b: ?a` -- solveable if `?b` is inferred to `'static`
33-
// - So the subtyping check succeeds, somewhat surprisingly.
34-
// This is because we can use `'static`.
22+
// "bidirectional" equality check, so we wind up with:
23+
// - `fn(fn(&?a u32)) == for<'b> fn(fn(&'b u32))` :-
24+
// - `fn(&!b u32) == fn(&?a u32)`
25+
// - `&?a u32 == &!b u32`
26+
// - `?a == !b` -- error.
27+
// - `fn(fn(&?a u32)) == for<'b> fn(fn(&'b u32))` :-
28+
// - `fn(&?b u32) == fn(&?a u32)`
29+
// - `&?a u32 == &?b u32`
30+
// - `?a == ?b` -- OK.
31+
// - So the unification fails.
3532

3633
foo::<()>();
34+
//~^ ERROR implementation of `Trait` is not general enough
3735
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
error: implementation of `Trait` is not general enough
2+
--> $DIR/hrtb-exists-forall-trait-covariant.rs:33:5
3+
|
4+
LL | foo::<()>();
5+
| ^^^^^^^^^^^ implementation of `Trait` is not general enough
6+
|
7+
= note: `()` must implement `Trait<for<'b> fn(fn(&'b u32))>`
8+
= note: ...but it actually implements `Trait<fn(fn(&'0 u32))>`, for some specific lifetime `'0`
9+
10+
error: aborting due to 1 previous error
11+

tests/ui/lub-glb/old-lub-glb-hr-eq.rs

+1-2
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,6 @@
33
// error. However, now that we handle subtyping correctly, we no
44
// longer get an error, because we recognize these two types as
55
// equivalent!
6-
//
7-
//@ check-pass
86

97
fn foo(x: fn(&u8, &u8), y: for<'a> fn(&'a u8, &'a u8)) {
108
// 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)) {
1311
let z = match 22 {
1412
0 => x,
1513
_ => y,
14+
//~^ ERROR `match` arms have incompatible types [E0308]
1615
};
1716
}
1817

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
error[E0308]: `match` arms have incompatible types
2+
--> $DIR/old-lub-glb-hr-eq.rs:13:14
3+
|
4+
LL | let z = match 22 {
5+
| _____________-
6+
LL | | 0 => x,
7+
| | - this is found to be of type `for<'a, 'b> fn(&'a u8, &'b u8)`
8+
LL | | _ => y,
9+
| | ^ one type is more general than the other
10+
LL | |
11+
LL | | };
12+
| |_____- `match` arms have incompatible types
13+
|
14+
= note: expected fn pointer `for<'a, 'b> fn(&'a _, &'b _)`
15+
found fn pointer `for<'a> fn(&'a _, &'a _)`
16+
17+
error: aborting due to 1 previous error
18+
19+
For more information about this error, try `rustc --explain E0308`.

tests/ui/traits/next-solver/member-constraints-in-root-universe.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
//@ compile-flags: -Znext-solver
2-
//@ check-pass
32

43
trait Trait {
54
type Ty;
@@ -11,6 +10,7 @@ impl Trait for for<'a> fn(&'a u8, &'a u8) {
1110

1211
// argument is necessary to create universes before registering the hidden type.
1312
fn test<'a>(_: <fn(&u8, &u8) as Trait>::Ty) -> impl Sized {
13+
//~^ ERROR the type `<for<'a, 'b> fn(&'a u8, &'b u8) as Trait>::Ty` is not well-formed
1414
"hidden type is `&'?0 str` with '?0 member of ['static,]"
1515
}
1616

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
error: the type `<for<'a, 'b> fn(&'a u8, &'b u8) as Trait>::Ty` is not well-formed
2+
--> $DIR/member-constraints-in-root-universe.rs:12:16
3+
|
4+
LL | fn test<'a>(_: <fn(&u8, &u8) as Trait>::Ty) -> impl Sized {
5+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^
6+
7+
error: aborting due to 1 previous error
8+

0 commit comments

Comments
 (0)