Rollup merge of #130866 - compiler-errors:dyn-instantiate-binder, r=lcnr
Allow instantiating object trait binder when upcasting This PR fixes two bugs (that probably need an FCP). ### We use equality rather than subtyping for upcasting dyn conversions This code should be valid: ```rust #![feature(trait_upcasting)] trait Foo: for<'h> Bar<'h> {} trait Bar<'a> {} fn foo(x: &dyn Foo) { let y: &dyn Bar<'static> = x; } ``` But instead: ``` error[E0308]: mismatched types --> src/lib.rs:7:32 | 7 | let y: &dyn Bar<'static> = x; | ^ one type is more general than the other | = note: expected existential trait ref `for<'h> Bar<'h>` found existential trait ref `Bar<'_>` ``` And so should this: ```rust #![feature(trait_upcasting)] fn foo(x: &dyn for<'h> Fn(&'h ())) { let y: &dyn FnOnce(&'static ()) = x; } ``` But instead: ``` error[E0308]: mismatched types --> src/lib.rs:4:39 | 4 | let y: &dyn FnOnce(&'static ()) = x; | ^ one type is more general than the other | = note: expected existential trait ref `for<'h> FnOnce<(&'h (),)>` found existential trait ref `FnOnce<(&(),)>` ``` Specifically, both of these fail because we use *equality* when comparing the supertrait to the *target* of the unsize goal. For the first example, since our supertrait is `for<'h> Bar<'h>` but our target is `Bar<'static>`, there's a higher-ranked type mismatch even though we *should* be able to instantiate that supertrait binder when upcasting. Similarly for the second example. ### New solver uses equality rather than subtyping for no-op (i.e. non-upcasting) dyn conversions This code should be valid in the new solver, like it is with the old solver: ```rust // -Znext-solver fn foo<'a>(x: &mut for<'h> dyn Fn(&'h ())) { let _: &mut dyn Fn(&'a ()) = x; } ``` But instead: ``` error: lifetime may not live long enough --> <source>:2:11 | 1 | fn foo<'a>(x: &mut dyn for<'h> Fn(&'h ())) { | -- lifetime `'a` defined here 2 | let _: &mut dyn Fn(&'a ()) = x; | ^^^^^^^^^^^^^^^^^^^ type annotation requires that `'a` must outlive `'static` | = note: requirement occurs because of a mutable reference to `dyn Fn(&())` ``` Specifically, this fails because we try to coerce `&mut dyn for<'h> Fn(&'h ())` to `&mut dyn Fn(&'a ())`, which registers an `dyn for<'h> Fn(&'h ()): dyn Fn(&'a ())` goal. This fails because the new solver uses *equating* rather than *subtyping* in `Unsize` goals. This is *mostly* not a problem... You may wonder why the same code passes on the new solver for immutable references: ``` // -Znext-solver fn foo<'a>(x: &dyn Fn(&())) { let _: &dyn Fn(&'a ()) = x; // works } ``` That's because in this case, we first try to coerce via `Unsize`, but due to the leak check the goal fails. Then, later in coercion, we fall back to a simple subtyping operation, which *does* work. Since `&T` is covariant over `T`, but `&mut T` is invariant, that's where the discrepancy between these two examples crops up. --- r? lcnr or reassign :D
This commit is contained in:
commit
4e510daed7
10 changed files with 229 additions and 202 deletions
|
@ -16,6 +16,7 @@ use rustc_hir::LangItem;
|
|||
use rustc_hir::def_id::DefId;
|
||||
use rustc_infer::infer::BoundRegionConversionTime::{self, HigherRankedType};
|
||||
use rustc_infer::infer::DefineOpaqueTypes;
|
||||
use rustc_infer::infer::at::ToTrace;
|
||||
use rustc_infer::infer::relate::TypeRelation;
|
||||
use rustc_infer::traits::TraitObligation;
|
||||
use rustc_middle::bug;
|
||||
|
@ -44,7 +45,7 @@ use super::{
|
|||
TraitQueryMode, const_evaluatable, project, util, wf,
|
||||
};
|
||||
use crate::error_reporting::InferCtxtErrorExt;
|
||||
use crate::infer::{InferCtxt, InferCtxtExt, InferOk, TypeFreshener};
|
||||
use crate::infer::{InferCtxt, InferOk, TypeFreshener};
|
||||
use crate::solve::InferCtxtSelectExt as _;
|
||||
use crate::traits::normalize::{normalize_with_depth, normalize_with_depth_to};
|
||||
use crate::traits::project::{ProjectAndUnifyResult, ProjectionCacheKeyExt};
|
||||
|
@ -2579,16 +2580,31 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
// Check that a_ty's supertrait (upcast_principal) is compatible
|
||||
// with the target (b_ty).
|
||||
ty::ExistentialPredicate::Trait(target_principal) => {
|
||||
let hr_source_principal = upcast_principal.map_bound(|trait_ref| {
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
||||
});
|
||||
let hr_target_principal = bound.rebind(target_principal);
|
||||
|
||||
nested.extend(
|
||||
self.infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.eq(
|
||||
DefineOpaqueTypes::Yes,
|
||||
upcast_principal.map_bound(|trait_ref| {
|
||||
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
||||
}),
|
||||
bound.rebind(target_principal),
|
||||
)
|
||||
.enter_forall(hr_target_principal, |target_principal| {
|
||||
let source_principal =
|
||||
self.infcx.instantiate_binder_with_fresh_vars(
|
||||
obligation.cause.span,
|
||||
HigherRankedType,
|
||||
hr_source_principal,
|
||||
);
|
||||
self.infcx.at(&obligation.cause, obligation.param_env).eq_trace(
|
||||
DefineOpaqueTypes::Yes,
|
||||
ToTrace::to_trace(
|
||||
&obligation.cause,
|
||||
hr_target_principal,
|
||||
hr_source_principal,
|
||||
),
|
||||
target_principal,
|
||||
source_principal,
|
||||
)
|
||||
})
|
||||
.map_err(|_| SelectionError::Unimplemented)?
|
||||
.into_obligations(),
|
||||
);
|
||||
|
@ -2599,19 +2615,40 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
// return ambiguity. Otherwise, if exactly one matches, equate
|
||||
// it with b_ty's projection.
|
||||
ty::ExistentialPredicate::Projection(target_projection) => {
|
||||
let target_projection = bound.rebind(target_projection);
|
||||
let hr_target_projection = bound.rebind(target_projection);
|
||||
|
||||
let mut matching_projections =
|
||||
a_data.projection_bounds().filter(|source_projection| {
|
||||
a_data.projection_bounds().filter(|&hr_source_projection| {
|
||||
// Eager normalization means that we can just use can_eq
|
||||
// here instead of equating and processing obligations.
|
||||
source_projection.item_def_id() == target_projection.item_def_id()
|
||||
&& self.infcx.can_eq(
|
||||
obligation.param_env,
|
||||
*source_projection,
|
||||
target_projection,
|
||||
)
|
||||
hr_source_projection.item_def_id() == hr_target_projection.item_def_id()
|
||||
&& self.infcx.probe(|_| {
|
||||
self.infcx
|
||||
.enter_forall(hr_target_projection, |target_projection| {
|
||||
let source_projection =
|
||||
self.infcx.instantiate_binder_with_fresh_vars(
|
||||
obligation.cause.span,
|
||||
HigherRankedType,
|
||||
hr_source_projection,
|
||||
);
|
||||
self.infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.eq_trace(
|
||||
DefineOpaqueTypes::Yes,
|
||||
ToTrace::to_trace(
|
||||
&obligation.cause,
|
||||
hr_target_projection,
|
||||
hr_source_projection,
|
||||
),
|
||||
target_projection,
|
||||
source_projection,
|
||||
)
|
||||
})
|
||||
.is_ok()
|
||||
})
|
||||
});
|
||||
let Some(source_projection) = matching_projections.next() else {
|
||||
|
||||
let Some(hr_source_projection) = matching_projections.next() else {
|
||||
return Err(SelectionError::Unimplemented);
|
||||
};
|
||||
if matching_projections.next().is_some() {
|
||||
|
@ -2619,8 +2656,24 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
|||
}
|
||||
nested.extend(
|
||||
self.infcx
|
||||
.at(&obligation.cause, obligation.param_env)
|
||||
.eq(DefineOpaqueTypes::Yes, source_projection, target_projection)
|
||||
.enter_forall(hr_target_projection, |target_projection| {
|
||||
let source_projection =
|
||||
self.infcx.instantiate_binder_with_fresh_vars(
|
||||
obligation.cause.span,
|
||||
HigherRankedType,
|
||||
hr_source_projection,
|
||||
);
|
||||
self.infcx.at(&obligation.cause, obligation.param_env).eq_trace(
|
||||
DefineOpaqueTypes::Yes,
|
||||
ToTrace::to_trace(
|
||||
&obligation.cause,
|
||||
hr_target_projection,
|
||||
hr_source_projection,
|
||||
),
|
||||
target_projection,
|
||||
source_projection,
|
||||
)
|
||||
})
|
||||
.map_err(|_| SelectionError::Unimplemented)?
|
||||
.into_obligations(),
|
||||
);
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue