Check object's supertrait and associated type bounds in new solver
This commit is contained in:
parent
07c993eba8
commit
98525aeee7
@ -99,6 +99,15 @@ pub(super) trait GoalKind<'tcx>: TypeFoldable<TyCtxt<'tcx>> + Copy + Eq {
|
||||
requirements: impl IntoIterator<Item = Goal<'tcx, ty::Predicate<'tcx>>>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
// Consider a clause specifically for a `dyn Trait` self type. This requires
|
||||
// additionally checking all of the supertraits and object bounds to hold,
|
||||
// since they're not implied by the well-formedness of the object type.
|
||||
fn consider_object_bound_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
assumption: ty::Predicate<'tcx>,
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
@ -455,7 +464,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
for assumption in
|
||||
elaborate_predicates(tcx, bounds.iter().map(|bound| bound.with_self_ty(tcx, self_ty)))
|
||||
{
|
||||
match G::consider_implied_clause(self, goal, assumption.predicate, []) {
|
||||
match G::consider_object_bound_candidate(self, goal, assumption.predicate) {
|
||||
Ok(result) => {
|
||||
candidates.push(Candidate { source: CandidateSource::BuiltinImpl, result })
|
||||
}
|
||||
|
@ -128,6 +128,50 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_object_bound_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
assumption: ty::Predicate<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(poly_projection_pred) = assumption.to_opt_poly_projection_pred()
|
||||
&& poly_projection_pred.projection_def_id() == goal.predicate.def_id()
|
||||
{
|
||||
ecx.probe(|ecx| {
|
||||
let assumption_projection_pred =
|
||||
ecx.instantiate_binder_with_infer(poly_projection_pred);
|
||||
let mut nested_goals = ecx.eq(
|
||||
goal.param_env,
|
||||
goal.predicate.projection_ty,
|
||||
assumption_projection_pred.projection_ty,
|
||||
)?;
|
||||
|
||||
let tcx = ecx.tcx();
|
||||
let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
|
||||
bug!("expected object type in `consider_object_bound_candidate`");
|
||||
};
|
||||
nested_goals.extend(
|
||||
structural_traits::predicates_for_object_candidate(
|
||||
tcx,
|
||||
goal.predicate.projection_ty.trait_ref(tcx),
|
||||
bounds,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|pred| goal.with(tcx, pred)),
|
||||
);
|
||||
|
||||
let subst_certainty = ecx.evaluate_all(nested_goals)?;
|
||||
|
||||
ecx.eq_term_and_make_canonical_response(
|
||||
goal,
|
||||
subst_certainty,
|
||||
assumption_projection_pred.term,
|
||||
)
|
||||
})
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_impl_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, ProjectionPredicate<'tcx>>,
|
||||
|
@ -86,6 +86,45 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_object_bound_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
assumption: ty::Predicate<'tcx>,
|
||||
) -> QueryResult<'tcx> {
|
||||
if let Some(poly_trait_pred) = assumption.to_opt_poly_trait_pred()
|
||||
&& poly_trait_pred.def_id() == goal.predicate.def_id()
|
||||
{
|
||||
// FIXME: Constness and polarity
|
||||
ecx.probe(|ecx| {
|
||||
let assumption_trait_pred =
|
||||
ecx.instantiate_binder_with_infer(poly_trait_pred);
|
||||
let mut nested_goals = ecx.eq(
|
||||
goal.param_env,
|
||||
goal.predicate.trait_ref,
|
||||
assumption_trait_pred.trait_ref,
|
||||
)?;
|
||||
|
||||
let tcx = ecx.tcx();
|
||||
let ty::Dynamic(bounds, _, _) = *goal.predicate.self_ty().kind() else {
|
||||
bug!("expected object type in `consider_object_bound_candidate`");
|
||||
};
|
||||
nested_goals.extend(
|
||||
structural_traits::predicates_for_object_candidate(
|
||||
tcx,
|
||||
goal.predicate.trait_ref,
|
||||
bounds,
|
||||
)
|
||||
.into_iter()
|
||||
.map(|pred| goal.with(tcx, pred)),
|
||||
);
|
||||
|
||||
ecx.evaluate_all_and_make_canonical_response(nested_goals)
|
||||
})
|
||||
} else {
|
||||
Err(NoSolution)
|
||||
}
|
||||
}
|
||||
|
||||
fn consider_auto_trait_candidate(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
|
@ -1,6 +1,7 @@
|
||||
use rustc_data_structures::fx::FxHashMap;
|
||||
use rustc_hir::{Movability, Mutability};
|
||||
use rustc_infer::traits::query::NoSolution;
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt};
|
||||
use rustc_middle::ty::{self, Ty, TyCtxt, TypeFoldable, TypeFolder, TypeSuperFoldable};
|
||||
|
||||
use crate::solve::EvalCtxt;
|
||||
|
||||
@ -233,3 +234,62 @@ pub(crate) fn extract_tupled_inputs_and_output_from_callable<'tcx>(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub(crate) fn predicates_for_object_candidate<'tcx>(
|
||||
tcx: TyCtxt<'tcx>,
|
||||
trait_ref: ty::TraitRef<'tcx>,
|
||||
object_bound: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||
) -> Vec<ty::Predicate<'tcx>> {
|
||||
let mut requirements = vec![];
|
||||
requirements.extend(
|
||||
tcx.super_predicates_of(trait_ref.def_id).instantiate(tcx, trait_ref.substs).predicates,
|
||||
);
|
||||
for item in tcx.associated_items(trait_ref.def_id).in_definition_order() {
|
||||
if item.kind == ty::AssocKind::Type {
|
||||
requirements.extend(tcx.item_bounds(item.def_id).subst(tcx, trait_ref.substs));
|
||||
}
|
||||
}
|
||||
|
||||
let mut replace_projection_with = FxHashMap::default();
|
||||
for bound in object_bound {
|
||||
let bound = bound.no_bound_vars().expect("higher-ranked projections not supported, yet");
|
||||
if let ty::ExistentialPredicate::Projection(proj) = bound {
|
||||
let proj = proj.with_self_ty(tcx, trait_ref.self_ty());
|
||||
let old_ty = replace_projection_with.insert(
|
||||
proj.projection_ty,
|
||||
proj.term.ty().expect("expected only types in dyn right now"),
|
||||
);
|
||||
assert_eq!(
|
||||
old_ty,
|
||||
None,
|
||||
"{} has two substitutions: {} and {}",
|
||||
proj.projection_ty,
|
||||
proj.term,
|
||||
old_ty.unwrap()
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
requirements.fold_with(&mut ReplaceProjectionWith { tcx, mapping: replace_projection_with })
|
||||
}
|
||||
|
||||
struct ReplaceProjectionWith<'tcx> {
|
||||
tcx: TyCtxt<'tcx>,
|
||||
mapping: FxHashMap<ty::AliasTy<'tcx>, Ty<'tcx>>,
|
||||
}
|
||||
|
||||
impl<'tcx> TypeFolder<TyCtxt<'tcx>> for ReplaceProjectionWith<'tcx> {
|
||||
fn interner(&self) -> TyCtxt<'tcx> {
|
||||
self.tcx
|
||||
}
|
||||
|
||||
fn fold_ty(&mut self, ty: Ty<'tcx>) -> Ty<'tcx> {
|
||||
if let ty::Alias(ty::Projection, alias_ty) = *ty.kind()
|
||||
&& let Some(replacement) = self.mapping.get(&alias_ty)
|
||||
{
|
||||
*replacement
|
||||
} else {
|
||||
ty.super_fold_with(self)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
20
tests/ui/traits/new-solver/object-unsafety.rs
Normal file
20
tests/ui/traits/new-solver/object-unsafety.rs
Normal file
@ -0,0 +1,20 @@
|
||||
// compile-flags: -Ztrait-solver=next
|
||||
|
||||
trait Setup {
|
||||
type From: Copy;
|
||||
}
|
||||
|
||||
fn copy<U: Setup + ?Sized>(from: &U::From) -> U::From {
|
||||
*from
|
||||
}
|
||||
|
||||
pub fn copy_any<T>(t: &T) -> T {
|
||||
copy::<dyn Setup<From=T>>(t)
|
||||
//~^ ERROR the trait bound `dyn Setup<From = T>: Setup` is not satisfied
|
||||
}
|
||||
|
||||
fn main() {
|
||||
let x = String::from("Hello, world");
|
||||
let y = copy_any(&x);
|
||||
println!("{y}");
|
||||
}
|
19
tests/ui/traits/new-solver/object-unsafety.stderr
Normal file
19
tests/ui/traits/new-solver/object-unsafety.stderr
Normal file
@ -0,0 +1,19 @@
|
||||
error[E0277]: the trait bound `dyn Setup<From = T>: Setup` is not satisfied
|
||||
--> $DIR/object-unsafety.rs:12:12
|
||||
|
|
||||
LL | copy::<dyn Setup<From=T>>(t)
|
||||
| ^^^^^^^^^^^^^^^^^ the trait `Setup` is not implemented for `dyn Setup<From = T>`
|
||||
|
|
||||
note: required by a bound in `copy`
|
||||
--> $DIR/object-unsafety.rs:7:12
|
||||
|
|
||||
LL | fn copy<U: Setup + ?Sized>(from: &U::From) -> U::From {
|
||||
| ^^^^^ required by this bound in `copy`
|
||||
help: consider introducing a `where` clause, but there might be an alternative better way to express this requirement
|
||||
|
|
||||
LL | pub fn copy_any<T>(t: &T) -> T where dyn Setup<From = T>: Setup {
|
||||
| ++++++++++++++++++++++++++++++++
|
||||
|
||||
error: aborting due to previous error
|
||||
|
||||
For more information about this error, try `rustc --explain E0277`.
|
Loading…
x
Reference in New Issue
Block a user