Skip to content

Commit da4e7bd

Browse files
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
2 parents 47bdda2 + 7d8563c commit da4e7bd

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(
@@ -574,43 +619,6 @@ impl<'tcx> EvalCtxt<'_, 'tcx> {
574619
responses
575620
}
576621

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