Separate consider_unsize_to_dyn_candidate from other unsize candidates
This commit is contained in:
parent
1b198b3a19
commit
7d8563c602
4 changed files with 94 additions and 54 deletions
|
@ -281,23 +281,27 @@ pub(super) trait GoalKind<'tcx>:
|
||||||
) -> QueryResult<'tcx>;
|
) -> QueryResult<'tcx>;
|
||||||
|
|
||||||
/// Consider (possibly several) candidates to upcast or unsize a type to another
|
/// Consider (possibly several) candidates to upcast or unsize a type to another
|
||||||
/// type.
|
/// type, excluding the coercion of a sized type into a `dyn Trait`.
|
||||||
///
|
|
||||||
/// The most common forms of unsizing are array to slice, and concrete (Sized)
|
|
||||||
/// type into a `dyn Trait`. ADTs and Tuples can also have their final field
|
|
||||||
/// unsized if it's generic.
|
|
||||||
///
|
|
||||||
/// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
|
|
||||||
/// if `Trait2` is a (transitive) supertrait of `Trait2`.
|
|
||||||
///
|
///
|
||||||
/// We return the `BuiltinImplSource` for each candidate as it is needed
|
/// We return the `BuiltinImplSource` for each candidate as it is needed
|
||||||
/// for unsize coercion in hir typeck and because it is difficult to
|
/// for unsize coercion in hir typeck and because it is difficult to
|
||||||
/// otherwise recompute this for codegen. This is a bit of a mess but the
|
/// otherwise recompute this for codegen. This is a bit of a mess but the
|
||||||
/// easiest way to maintain the existing behavior for now.
|
/// easiest way to maintain the existing behavior for now.
|
||||||
fn consider_builtin_unsize_candidates(
|
fn consider_structural_builtin_unsize_candidates(
|
||||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
|
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
|
||||||
|
|
||||||
|
/// Consider the `Unsize` candidate corresponding to coercing a sized type
|
||||||
|
/// into a `dyn Trait`.
|
||||||
|
///
|
||||||
|
/// This is computed separately from the rest of the `Unsize` candidates
|
||||||
|
/// since it is only done once per self type, and not once per
|
||||||
|
/// *normalization step* (in `assemble_candidates_via_self_ty`).
|
||||||
|
fn consider_unsize_to_dyn_candidate(
|
||||||
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
|
goal: Goal<'tcx, Self>,
|
||||||
|
) -> QueryResult<'tcx>;
|
||||||
}
|
}
|
||||||
|
|
||||||
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
@ -312,6 +316,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
|
|
||||||
let mut candidates = self.assemble_candidates_via_self_ty(goal, 0);
|
let mut candidates = self.assemble_candidates_via_self_ty(goal, 0);
|
||||||
|
|
||||||
|
self.assemble_unsize_to_dyn_candidate(goal, &mut candidates);
|
||||||
|
|
||||||
self.assemble_blanket_impl_candidates(goal, &mut candidates);
|
self.assemble_blanket_impl_candidates(goal, &mut candidates);
|
||||||
|
|
||||||
self.assemble_param_env_candidates(goal, &mut candidates);
|
self.assemble_param_env_candidates(goal, &mut candidates);
|
||||||
|
@ -530,6 +536,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn assemble_unsize_to_dyn_candidate<G: GoalKind<'tcx>>(
|
||||||
|
&mut self,
|
||||||
|
goal: Goal<'tcx, G>,
|
||||||
|
candidates: &mut Vec<Candidate<'tcx>>,
|
||||||
|
) {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
if tcx.lang_items().unsize_trait() == Some(goal.predicate.trait_def_id(tcx)) {
|
||||||
|
match G::consider_unsize_to_dyn_candidate(self, goal) {
|
||||||
|
Ok(result) => candidates.push(Candidate {
|
||||||
|
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
|
||||||
|
result,
|
||||||
|
}),
|
||||||
|
Err(NoSolution) => (),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
|
fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
|
||||||
&mut self,
|
&mut self,
|
||||||
goal: Goal<'tcx, G>,
|
goal: Goal<'tcx, G>,
|
||||||
|
@ -610,7 +633,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
// There may be multiple unsize candidates for a trait with several supertraits:
|
// There may be multiple unsize candidates for a trait with several supertraits:
|
||||||
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
|
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
|
||||||
if lang_items.unsize_trait() == Some(trait_def_id) {
|
if lang_items.unsize_trait() == Some(trait_def_id) {
|
||||||
for (result, source) in G::consider_builtin_unsize_candidates(self, goal) {
|
for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) {
|
||||||
candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
|
candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -497,7 +497,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn consider_builtin_unsize_candidates(
|
fn consider_unsize_to_dyn_candidate(
|
||||||
|
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
|
goal: Goal<'tcx, Self>,
|
||||||
|
) -> QueryResult<'tcx> {
|
||||||
|
bug!("`Unsize` does not have an associated type: {:?}", goal)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn consider_structural_builtin_unsize_candidates(
|
||||||
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
_ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
|
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
|
||||||
|
|
|
@ -423,7 +423,55 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
|
||||||
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
|
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>,
|
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||||
goal: Goal<'tcx, Self>,
|
goal: Goal<'tcx, Self>,
|
||||||
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
|
) -> 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,
|
goal, a_data, a_region, b_data, b_region,
|
||||||
),
|
),
|
||||||
|
|
||||||
// `T` -> `dyn Trait` unsizing
|
// `T` -> `dyn Trait` unsizing is handled separately in `consider_unsize_to_dyn_candidate`
|
||||||
(_, &ty::Dynamic(b_data, b_region, ty::Dyn)) => result_to_single(
|
(_, &ty::Dynamic(..)) => vec![],
|
||||||
ecx.consider_builtin_unsize_to_dyn(goal, b_data, b_region),
|
|
||||||
BuiltinImplSource::Misc,
|
|
||||||
),
|
|
||||||
|
|
||||||
// `[T; N]` -> `[T]` unsizing
|
// `[T; N]` -> `[T]` unsizing
|
||||||
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single(
|
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single(
|
||||||
|
@ -572,43 +617,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||||
responses
|
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(
|
fn consider_builtin_upcast_to_principal(
|
||||||
&mut self,
|
&mut self,
|
||||||
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// revisions: current next
|
||||||
|
//[next] compile-flags: -Ztrait-solver=next
|
||||||
// check-pass
|
// check-pass
|
||||||
|
|
||||||
trait Trait {}
|
trait Trait {}
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue