diff --git a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs index 5bfa8348600ee..36194f973b572 100644 --- a/compiler/rustc_trait_selection/src/solve/assembly/mod.rs +++ b/compiler/rustc_trait_selection/src/solve/assembly/mod.rs @@ -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>( + &mut self, + goal: Goal<'tcx, G>, + candidates: &mut Vec>, + ) { + 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>( &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 + Bar` and `dyn Foo: Unsize>` 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 }); } } diff --git a/compiler/rustc_trait_selection/src/solve/project_goals.rs b/compiler/rustc_trait_selection/src/solve/project_goals.rs index e1980f4d7bb0e..6117f51a37885 100644 --- a/compiler/rustc_trait_selection/src/solve/project_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/project_goals.rs @@ -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)> { diff --git a/compiler/rustc_trait_selection/src/solve/trait_goals.rs b/compiler/rustc_trait_selection/src/solve/trait_goals.rs index 8bab20e19421d..8685f3100a822 100644 --- a/compiler/rustc_trait_selection/src/solve/trait_goals.rs +++ b/compiler/rustc_trait_selection/src/solve/trait_goals.rs @@ -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 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 for T {} - /// ``` - fn consider_builtin_unsize_to_dyn( - &mut self, - goal: Goal<'tcx, (Ty<'tcx>, Ty<'tcx>)>, - b_data: &'tcx ty::List>, - 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>)>, diff --git a/tests/ui/unsized/issue-75899.rs b/tests/ui/unsized/issue-75899.rs index abff17e11b54a..7194310329157 100644 --- a/tests/ui/unsized/issue-75899.rs +++ b/tests/ui/unsized/issue-75899.rs @@ -1,3 +1,5 @@ +// revisions: current next +//[next] compile-flags: -Ztrait-solver=next // check-pass trait Trait {}