Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Separate consider_unsize_to_dyn_candidate from other unsize candidates #114829

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 33 additions & 10 deletions compiler/rustc_trait_selection/src/solve/assembly/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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> {
Expand All @@ -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);
Expand Down Expand Up @@ -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>,
Expand Down Expand Up @@ -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 });
}
}
Expand Down
9 changes: 8 additions & 1 deletion compiler/rustc_trait_selection/src/solve/project_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)> {
Expand Down
94 changes: 51 additions & 43 deletions compiler/rustc_trait_selection/src/solve/trait_goals.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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)> {
Expand Down Expand Up @@ -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(
Expand Down Expand Up @@ -572,43 +617,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>)>,
Expand Down
2 changes: 2 additions & 0 deletions tests/ui/unsized/issue-75899.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
// revisions: current next
//[next] compile-flags: -Ztrait-solver=next
// check-pass

trait Trait {}
Expand Down