Skip to content

Commit 7d8563c

Browse files
Separate consider_unsize_to_dyn_candidate from other unsize candidates
1 parent 1b198b3 commit 7d8563c

File tree

4 files changed

+94
-54
lines changed

4 files changed

+94
-54
lines changed

compiler/rustc_trait_selection/src/solve/assembly/mod.rs

+33-10
Original file line numberDiff line numberDiff line change
@@ -281,23 +281,27 @@ pub(super) trait GoalKind<'tcx>:
281281
) -> QueryResult<'tcx>;
282282

283283
/// Consider (possibly several) candidates to upcast or unsize a type to another
284-
/// type.
285-
///
286-
/// The most common forms of unsizing are array to slice, and concrete (Sized)
287-
/// type into a `dyn Trait`. ADTs and Tuples can also have their final field
288-
/// unsized if it's generic.
289-
///
290-
/// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
291-
/// if `Trait2` is a (transitive) supertrait of `Trait2`.
284+
/// type, excluding the coercion of a sized type into a `dyn Trait`.
292285
///
293286
/// We return the `BuiltinImplSource` for each candidate as it is needed
294287
/// for unsize coercion in hir typeck and because it is difficult to
295288
/// otherwise recompute this for codegen. This is a bit of a mess but the
296289
/// easiest way to maintain the existing behavior for now.
297-
fn consider_builtin_unsize_candidates(
290+
fn consider_structural_builtin_unsize_candidates(
298291
ecx: &mut EvalCtxt<'_, 'tcx>,
299292
goal: Goal<'tcx, Self>,
300293
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
294+
295+
/// Consider the `Unsize` candidate corresponding to coercing a sized type
296+
/// into a `dyn Trait`.
297+
///
298+
/// This is computed separately from the rest of the `Unsize` candidates
299+
/// since it is only done once per self type, and not once per
300+
/// *normalization step* (in `assemble_candidates_via_self_ty`).
301+
fn consider_unsize_to_dyn_candidate(
302+
ecx: &mut EvalCtxt<'_, 'tcx>,
303+
goal: Goal<'tcx, Self>,
304+
) -> QueryResult<'tcx>;
301305
}
302306

303307
impl<'tcx> EvalCtxt<'_, 'tcx> {
@@ -312,6 +316,8 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
312316

313317
let mut candidates = self.assemble_candidates_via_self_ty(goal, 0);
314318

319+
self.assemble_unsize_to_dyn_candidate(goal, &mut candidates);
320+
315321
self.assemble_blanket_impl_candidates(goal, &mut candidates);
316322

317323
self.assemble_param_env_candidates(goal, &mut candidates);
@@ -530,6 +536,23 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
530536
}
531537
}
532538

539+
fn assemble_unsize_to_dyn_candidate<G: GoalKind<'tcx>>(
540+
&mut self,
541+
goal: Goal<'tcx, G>,
542+
candidates: &mut Vec<Candidate<'tcx>>,
543+
) {
544+
let tcx = self.tcx();
545+
if tcx.lang_items().unsize_trait() == Some(goal.predicate.trait_def_id(tcx)) {
546+
match G::consider_unsize_to_dyn_candidate(self, goal) {
547+
Ok(result) => candidates.push(Candidate {
548+
source: CandidateSource::BuiltinImpl(BuiltinImplSource::Misc),
549+
result,
550+
}),
551+
Err(NoSolution) => (),
552+
}
553+
}
554+
}
555+
533556
fn assemble_blanket_impl_candidates<G: GoalKind<'tcx>>(
534557
&mut self,
535558
goal: Goal<'tcx, G>,
@@ -610,7 +633,7 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
610633
// There may be multiple unsize candidates for a trait with several supertraits:
611634
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
612635
if lang_items.unsize_trait() == Some(trait_def_id) {
613-
for (result, source) in G::consider_builtin_unsize_candidates(self, goal) {
636+
for (result, source) in G::consider_structural_builtin_unsize_candidates(self, goal) {
614637
candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
615638
}
616639
}

compiler/rustc_trait_selection/src/solve/project_goals.rs

+8-1
Original file line numberDiff line numberDiff line change
@@ -497,7 +497,14 @@ impl<'tcx> assembly::GoalKind<'tcx> for ProjectionPredicate<'tcx> {
497497
)
498498
}
499499

500-
fn consider_builtin_unsize_candidates(
500+
fn consider_unsize_to_dyn_candidate(
501+
_ecx: &mut EvalCtxt<'_, 'tcx>,
502+
goal: Goal<'tcx, Self>,
503+
) -> QueryResult<'tcx> {
504+
bug!("`Unsize` does not have an associated type: {:?}", goal)
505+
}
506+
507+
fn consider_structural_builtin_unsize_candidates(
501508
_ecx: &mut EvalCtxt<'_, 'tcx>,
502509
goal: Goal<'tcx, Self>,
503510
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {

compiler/rustc_trait_selection/src/solve/trait_goals.rs

+51-43
Original file line numberDiff line numberDiff line change
@@ -423,7 +423,55 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
423423
ecx.evaluate_added_goals_and_make_canonical_response(certainty)
424424
}
425425

426-
fn consider_builtin_unsize_candidates(
426+
fn consider_unsize_to_dyn_candidate(
427+
ecx: &mut EvalCtxt<'_, 'tcx>,
428+
goal: Goal<'tcx, Self>,
429+
) -> QueryResult<'tcx> {
430+
ecx.probe(|_| CandidateKind::UnsizeAssembly).enter(|ecx| {
431+
let a_ty = goal.predicate.self_ty();
432+
// We need to normalize the b_ty since it's destructured as a `dyn Trait`.
433+
let Some(b_ty) =
434+
ecx.try_normalize_ty(goal.param_env, goal.predicate.trait_ref.args.type_at(1))?
435+
else {
436+
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::OVERFLOW);
437+
};
438+
439+
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
440+
return Err(NoSolution);
441+
};
442+
443+
let tcx = ecx.tcx();
444+
445+
// Can only unsize to an object-safe trait.
446+
if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
447+
return Err(NoSolution);
448+
}
449+
450+
// Check that the type implements all of the predicates of the trait object.
451+
// (i.e. the principal, all of the associated types match, and any auto traits)
452+
ecx.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))));
453+
454+
// The type must be `Sized` to be unsized.
455+
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
456+
ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
457+
} else {
458+
return Err(NoSolution);
459+
}
460+
461+
// The type must outlive the lifetime of the `dyn` we're unsizing into.
462+
ecx.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
463+
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
464+
})
465+
}
466+
467+
/// ```ignore (builtin impl example)
468+
/// trait Trait {
469+
/// fn foo(&self);
470+
/// }
471+
/// // results in the following builtin impl
472+
/// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
473+
/// ```
474+
fn consider_structural_builtin_unsize_candidates(
427475
ecx: &mut EvalCtxt<'_, 'tcx>,
428476
goal: Goal<'tcx, Self>,
429477
) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
@@ -468,11 +516,8 @@ impl<'tcx> assembly::GoalKind<'tcx> for TraitPredicate<'tcx> {
468516
goal, a_data, a_region, b_data, b_region,
469517
),
470518

471-
// `T` -> `dyn Trait` unsizing
472-
(_, &ty::Dynamic(b_data, b_region, ty::Dyn)) => result_to_single(
473-
ecx.consider_builtin_unsize_to_dyn(goal, b_data, b_region),
474-
BuiltinImplSource::Misc,
475-
),
519+
// `T` -> `dyn Trait` unsizing is handled separately in `consider_unsize_to_dyn_candidate`
520+
(_, &ty::Dynamic(..)) => vec![],
476521

477522
// `[T; N]` -> `[T]` unsizing
478523
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => result_to_single(
@@ -572,43 +617,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
572617
responses
573618
}
574619

575-
/// ```ignore (builtin impl example)
576-
/// trait Trait {
577-
/// fn foo(&self);
578-
/// }
579-
/// // results in the following builtin impl
580-
/// impl<'a, T: Trait + 'a> Unsize<dyn Trait + 'a> for T {}
581-
/// ```
582-
fn consider_builtin_unsize_to_dyn(
583-
&mut self,
584-
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,
585-
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
586-
b_region: ty::Region<'tcx>,
587-
) -> QueryResult<'tcx> {
588-
let tcx = self.tcx();
589-
let Goal { predicate: (a_ty, _b_ty), .. } = goal;
590-
591-
// Can only unsize to an object-safe trait
592-
if b_data.principal_def_id().is_some_and(|def_id| !tcx.check_is_object_safe(def_id)) {
593-
return Err(NoSolution);
594-
}
595-
596-
// Check that the type implements all of the predicates of the trait object.
597-
// (i.e. the principal, all of the associated types match, and any auto traits)
598-
self.add_goals(b_data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))));
599-
600-
// The type must be `Sized` to be unsized.
601-
if let Some(sized_def_id) = tcx.lang_items().sized_trait() {
602-
self.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
603-
} else {
604-
return Err(NoSolution);
605-
}
606-
607-
// The type must outlive the lifetime of the `dyn` we're unsizing into.
608-
self.add_goal(goal.with(tcx, ty::OutlivesPredicate(a_ty, b_region)));
609-
self.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
610-
}
611-
612620
fn consider_builtin_upcast_to_principal(
613621
&mut self,
614622
goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>,

tests/ui/unsized/issue-75899.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
// revisions: current next
2+
//[next] compile-flags: -Ztrait-solver=next
13
// check-pass
24

35
trait Trait {}

0 commit comments

Comments
 (0)