Rollup merge of #114829 - compiler-errors:next-solver-only-unsize-to-dyn-once, r=lcnr

Separate `consider_unsize_to_dyn_candidate` from other unsize candidates

Move the unsize candidate assembly *just for* `T -> dyn Trait` out of `assemble_candidates_via_self_ty` so that we only consider it once, instead of for every normalization step of the self ty. This makes sure that we don't assemble several candidates that are equal modulo normalization when we really don't care about normalizing the self type of an `T: Unsize<dyn Trait>` goal anyways.

Fixes rust-lang/trait-system-refactor-initiative#57

r? lcnr
This commit is contained in:
Guillaume Gomez
2023-08-15 14:29:49 +02:00
committed by GitHub
4 changed files with 94 additions and 54 deletions

View File

@@ -423,7 +423,55 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
}
fn consider_builtin_unsize_candidates(
fn consider_unsize_to_dyn_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> {
ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| {
let a_ty = goal.predicate.self_ty();
// We need to normalize the b_ty since it's destructured as a `dyn Trait`.
let Some(b_ty) =
ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))?
else {
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
};
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
return Err(NoSolution);
};
let tcx = ecx.tcx();
// Can only unsize to an object-safe trait.
if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
return Err(NoSolution);
}
// Check that the type implements all of the predicates of the trait object.
// (i.e. the principal, all of the associated types match, and any auto traits)
ecx.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))));
// The type must be `Sized` to be unsized.
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
} else {
return Err(NoSolution);
}
// The type must outlive the lifetime of the `dyn` we're unsizing into.
ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
}
/// ```ignore (builtin impl example)
/// trait Trait {
/// fn foo(&self);
/// }
/// // results in the following builtin impl
/// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
/// ```
fn consider_structural_builtin_unsize_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
@@ -468,11 +516,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
goal, a_data, a_region, b_data, b_region,
),
// `T` -> `dyn Trait` unsizing
(_, &ty::Dynamic(b_data, b_region, ty::Dyn)) => result_to_single(
ecx.consider_builtin_unsize_to_dyn(goal, b_data, b_region),
BuiltinImplSource::Misc,
),
// `T` -> `dyn Trait` unsizing is handled separately in `consider_unsize_to_dyn_candidate`
(_, &ty::Dynamic(..)) => vec![],
// `[T; N]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single(
@@ -574,43 +619,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
responses
}
/// ```ignore (builtin impl example)
/// trait Trait {
/// fn foo(&self);
/// }
/// // results in the following builtin impl
/// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
/// ```
fn consider_builtin_unsize_to_dyn(
&mut self,
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
b_region: ty::Region<'tcx>,
) -> QueryResult<'tcx> {
let tcx = self.tcx();
let Goal { predicate: (a_ty, _b_ty), .. } = goal;
// Can only unsize to an object-safe trait
if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
return Err(NoSolution);
}
// Check that the type implements all of the predicates of the trait object.
// (i.e. the principal, all of the associated types match, and any auto traits)
self.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))));
// The type must be `Sized` to be unsized.
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
self.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
} else {
return Err(NoSolution);
}
// The type must outlive the lifetime of the `dyn` we're unsizing into.
self.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
fn consider_builtin_upcast_to_principal(
&mut self,
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,