mirror of
https://github.com/rust-lang/rust.git
synced 2024-11-23 15:23:46 +00:00
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:
commit
da4e7bd0cd
@ -281,23 +281,27 @@ pub(super) trait GoalKind<'tcx>:
|
||||
) -> QueryResult<'tcx>;
|
||||
|
||||
/// Consider (possibly several) candidates to upcast or unsize a type to another
|
||||
/// type.
|
||||
///
|
||||
/// 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`.
|
||||
/// type, excluding the coercion of a sized type into a `dyn Trait`.
|
||||
///
|
||||
/// We return the `BuiltinImplSource` for each candidate as it is needed
|
||||
/// 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
|
||||
/// easiest way to maintain the existing behavior for now.
|
||||
fn consider_builtin_unsize_candidates(
|
||||
fn consider_structural_builtin_unsize_candidates(
|
||||
ecx: &mut EvalCtxt<'_, 'tcx>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> 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> {
|
||||
@ -312,6 +316,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
|
||||
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_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>>(
|
||||
&mut self,
|
||||
goal: Goal<'tcx, G>,
|
||||
@ -610,7 +633,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
|
||||
// There may be multiple unsize candidates for a trait with several supertraits:
|
||||
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
|
||||
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 });
|
||||
}
|
||||
}
|
||||
|
@ -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>,
|
||||
goal: Goal<'tcx, Self>,
|
||||
) -> 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)
|
||||
}
|
||||
|
||||
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>)>,
|
||||
|
@ -1,3 +1,5 @@
|
||||
// revisions: current next
|
||||
//[next] compile-flags: -Ztrait-solver=next
|
||||
// check-pass
|
||||
|
||||
trait Trait {}
|
||||
|
Loading…
Reference in New Issue
Block a user