Consolidate trait upcasting and unsize into one normalization

This commit is contained in:
Michael Goulet 2023-07-20 18:36:34 +00:00
parent c02d1a6553
commit de81007d13
4 changed files with 272 additions and 232 deletions

View File

@ -94,6 +94,7 @@ pub(super) enum CandidateSource {
#[derive(Debug, Clone, Copy)] #[derive(Debug, Clone, Copy)]
pub(super) enum BuiltinImplSource { pub(super) enum BuiltinImplSource {
TraitUpcasting, TraitUpcasting,
TupleUnsize,
Object, Object,
Misc, Misc,
Ambiguity, Ambiguity,
@ -281,20 +282,19 @@ fn consider_builtin_generator_candidate(
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>; ) -> QueryResult<'tcx>;
/// Consider (possibly several) goals to upcast or unsize a type to another
/// type.
///
/// The most common forms of unsizing are array to slice, and concrete (Sized) /// 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 /// type into a `dyn Trait`. ADTs and Tuples can also have their final field
/// unsized if it's generic. /// unsized if it's generic.
fn consider_builtin_unsize_candidate( ///
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx>;
/// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or /// `dyn Trait1` can be unsized to `dyn Trait2` if they are the same trait, or
/// if `Trait2` is a (transitive) supertrait of `Trait2`. /// if `Trait2` is a (transitive) supertrait of `Trait2`.
fn consider_builtin_dyn_upcast_candidates( fn consider_builtin_unsize_and_upcast_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>, _ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> Vec<CanonicalResponse<'tcx>>; ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)>;
fn consider_builtin_discriminant_kind_candidate( fn consider_builtin_discriminant_kind_candidate(
ecx: &mut EvalCtxt<'_, 'tcx>, ecx: &mut EvalCtxt<'_, 'tcx>,
@ -610,8 +610,6 @@ fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
G::consider_builtin_future_candidate(self, goal) G::consider_builtin_future_candidate(self, goal)
} else if lang_items.gen_trait() == Some(trait_def_id) { } else if lang_items.gen_trait() == Some(trait_def_id) {
G::consider_builtin_generator_candidate(self, goal) G::consider_builtin_generator_candidate(self, goal)
} else if lang_items.unsize_trait() == Some(trait_def_id) {
G::consider_builtin_unsize_candidate(self, goal)
} else if lang_items.discriminant_kind_trait() == Some(trait_def_id) { } else if lang_items.discriminant_kind_trait() == Some(trait_def_id) {
G::consider_builtin_discriminant_kind_candidate(self, goal) G::consider_builtin_discriminant_kind_candidate(self, goal)
} else if lang_items.destruct_trait() == Some(trait_def_id) { } else if lang_items.destruct_trait() == Some(trait_def_id) {
@ -633,11 +631,8 @@ fn assemble_builtin_impl_candidates<G: GoalKind<'tcx>>(
// There may be multiple unsize candidates for a trait with several supertraits: // There may be multiple unsize candidates for a trait with several supertraits:
// `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>` // `trait Foo: Bar<A> + Bar<B>` and `dyn Foo: Unsize<dyn Bar<_>>`
if lang_items.unsize_trait() == Some(trait_def_id) { if lang_items.unsize_trait() == Some(trait_def_id) {
for result in G::consider_builtin_dyn_upcast_candidates(self, goal) { for (result, source) in G::consider_builtin_unsize_and_upcast_candidates(self, goal) {
candidates.push(Candidate { candidates.push(Candidate { source: CandidateSource::BuiltinImpl(source), result });
source: CandidateSource::BuiltinImpl(BuiltinImplSource::TraitUpcasting),
result,
});
} }
} }
} }

View File

@ -116,10 +116,19 @@ fn select_in_new_trait_solver(
), ),
) => rematch_object(self, goal, nested_obligations), ) => rematch_object(self, goal, nested_obligations),
(Certainty::Maybe(_), CandidateSource::BuiltinImpl(BuiltinImplSource::Misc)) (
Certainty::Maybe(_),
CandidateSource::BuiltinImpl(
BuiltinImplSource::Misc | BuiltinImplSource::TupleUnsize,
),
) if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => {
rematch_unsize(self, goal, nested_obligations)
}
(Certainty::Yes, CandidateSource::BuiltinImpl(BuiltinImplSource::TupleUnsize))
if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) => if self.tcx.lang_items().unsize_trait() == Some(goal.predicate.def_id()) =>
{ {
rematch_unsize(self, goal, nested_obligations) Ok(Some(ImplSource::TupleUnsizing(nested_obligations)))
} }
// Technically some builtin impls have nested obligations, but if // Technically some builtin impls have nested obligations, but if

View File

@ -1,6 +1,6 @@
use crate::traits::specialization_graph; use crate::traits::specialization_graph;
use super::assembly::{self, structural_traits}; use super::assembly::{self, structural_traits, BuiltinImplSource};
use super::EvalCtxt; use super::EvalCtxt;
use rustc_hir::def::DefKind; use rustc_hir::def::DefKind;
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -502,17 +502,10 @@ fn consider_builtin_generator_candidate(
) )
} }
fn consider_builtin_unsize_candidate( fn consider_builtin_unsize_and_upcast_candidates(
_ecx: &mut EvalCtxt<'_, 'tcx>, _ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> { ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
bug!("`Unsize` does not have an associated type: {:?}", goal);
}
fn consider_builtin_dyn_upcast_candidates(
_ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<CanonicalResponse<'tcx>> {
bug!("`Unsize` does not have an associated type: {:?}", goal); bug!("`Unsize` does not have an associated type: {:?}", goal);
} }

View File

@ -1,6 +1,6 @@
//! Dealing with trait goals, i.e. `T: Trait<'a, U>`. //! Dealing with trait goals, i.e. `T: Trait<'a, U>`.
use super::assembly::{self, structural_traits}; use super::assembly::{self, structural_traits, BuiltinImplSource};
use super::search_graph::OverflowHandler; use super::search_graph::OverflowHandler;
use super::{EvalCtxt, SolverMode}; use super::{EvalCtxt, SolverMode};
use rustc_hir::def_id::DefId; use rustc_hir::def_id::DefId;
@ -8,7 +8,7 @@
use rustc_infer::traits::query::NoSolution; use rustc_infer::traits::query::NoSolution;
use rustc_infer::traits::util::supertraits; use rustc_infer::traits::util::supertraits;
use rustc_middle::traits::solve::inspect::CandidateKind; use rustc_middle::traits::solve::inspect::CandidateKind;
use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, MaybeCause, QueryResult}; use rustc_middle::traits::solve::{CanonicalResponse, Certainty, Goal, QueryResult};
use rustc_middle::traits::Reveal; use rustc_middle::traits::Reveal;
use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections}; use rustc_middle::ty::fast_reject::{DeepRejectCtxt, TreatParams, TreatProjections};
use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt}; use rustc_middle::ty::{self, ToPredicate, Ty, TyCtxt};
@ -367,220 +367,55 @@ fn consider_builtin_generator_candidate(
) )
} }
fn consider_builtin_unsize_candidate( fn consider_builtin_unsize_and_upcast_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>, ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>, goal: Goal<'tcx, Self>,
) -> QueryResult<'tcx> { ) -> Vec<(CanonicalResponse<'tcx>, BuiltinImplSource)> {
if goal.predicate.polarity != ty::ImplPolarity::Positive {
return Err(NoSolution);
}
let tcx = ecx.tcx();
let a_ty = goal.predicate.self_ty();
let b_ty = goal.predicate.trait_ref.args.type_at(1);
ecx.probe_candidate("builtin unsize").enter(|ecx| {
let Some(b_ty) = ecx.normalize_non_self_ty(b_ty, goal.param_env)? else {
return ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Maybe(
MaybeCause::Overflow,
));
};
match (a_ty.kind(), b_ty.kind()) {
(_, ty::Infer(ty::TyVar(_))) => {
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
}
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
// Dyn upcasting is handled separately, since due to upcasting,
// when there are two supertraits that differ by args, we
// may return more than one query response.
Err(NoSolution)
}
// `T` -> `dyn Trait` unsizing
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
// Can only unsize to an object-safe type
if data
.principal_def_id()
.is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
{
return Err(NoSolution);
}
let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
return Err(NoSolution);
};
// Check that the type implements all of the predicates of the def-id.
// (i.e. the principal, all of the associated types match, and any auto traits)
ecx.add_goals(
data.iter().map(|pred| goal.with(tcx, pred.with_self_ty(tcx, a_ty))),
);
// The type must be Sized to be unsized.
ecx.add_goal(goal.with(tcx, ty::TraitRef::new(tcx, sized_def_id, [a_ty])));
// The type must outlive the lifetime of the `dyn` we're unsizing into.
ecx.add_goal(
goal.with(tcx, ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region))),
);
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// `[T; n]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
// We just require that the element type stays the same
ecx.eq(goal.param_env, a_elem_ty, b_elem_ty)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
if a_def.is_struct() && a_def.did() == b_def.did() =>
{
let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
// We must be unsizing some type parameters. This also implies
// that the struct has a tail field.
if unsizing_params.is_empty() {
return Err(NoSolution);
}
let tail_field = a_def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
// Substitute just the unsizing params from B into A. The type after
// this substitution must be equal to B. This is so we don't unsize
// unrelated type parameters.
let new_a_args =
tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
if unsizing_params.contains(i as u32) { b_args[i] } else { a }
}));
let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args);
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
// types.
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
ecx.add_goal(goal.with(
tcx,
ty::TraitRef::new(tcx, goal.predicate.def_id(), [a_tail_ty, b_tail_ty]),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
{
let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
let b_last_ty = b_tys.last().unwrap();
// Substitute just the tail field of B., and require that they're equal.
let unsized_a_ty =
Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
ecx.eq(goal.param_env, unsized_a_ty, b_ty)?;
// Similar to ADTs, require that the rest of the fields are equal.
ecx.add_goal(goal.with(
tcx,
ty::TraitRef::new(tcx, goal.predicate.def_id(), [*a_last_ty, *b_last_ty]),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
_ => Err(NoSolution),
}
})
}
fn consider_builtin_dyn_upcast_candidates(
ecx: &mut EvalCtxt<'_, 'tcx>,
goal: Goal<'tcx, Self>,
) -> Vec<CanonicalResponse<'tcx>> {
if goal.predicate.polarity != ty::ImplPolarity::Positive { if goal.predicate.polarity != ty::ImplPolarity::Positive {
return vec![]; return vec![];
} }
let tcx = ecx.tcx();
// Need to wrap in a probe since `normalize_non_self_ty` has side-effects.
ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| { ecx.probe(|_| CandidateKind::DynUpcastingAssembly).enter(|ecx| {
let a_ty = goal.predicate.self_ty(); let a_ty = goal.predicate.self_ty();
let b_ty = goal.predicate.trait_ref.args.type_at(1); // We need to normalize the b_ty since it's matched structurally
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else { // in the other functions below.
return vec![]; let b_ty = match ecx
}; .normalize_non_self_ty(goal.predicate.trait_ref.args.type_at(1), goal.param_env)
// We don't care about `ty::Infer` here or errors here, since we'll
// register an ambiguous/error response in the other unsize candidate
// assembly function.
let Ok(Some(b_ty)) = ecx.normalize_non_self_ty(b_ty, goal.param_env) else {
return vec![];
};
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
return vec![];
};
// All of a's auto traits need to be in b's auto traits.
let auto_traits_compatible =
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
if !auto_traits_compatible {
return vec![];
}
let mut unsize_dyn_to_principal = |principal: Option<
ty::PolyExistentialTraitRef<'tcx>,
>| {
ecx.probe_candidate("upcast dyn to principle").enter(
|ecx| -> Result<_, NoSolution> {
// Require that all of the trait predicates from A match B, except for
// the auto traits. We do this by constructing a new A type with B's
// auto traits, and equating these types.
let new_a_data = principal
.into_iter()
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
.chain(a_data.iter().filter(|a| {
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
}))
.chain(
b_data
.auto_traits()
.map(ty::ExistentialPredicate::AutoTrait)
.map(ty::Binder::dummy),
);
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
// We also require that A's lifetime outlives B's lifetime.
ecx.eq(goal.param_env, new_a_ty, b_ty)?;
ecx.add_goal(goal.with(
tcx,
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
},
)
};
let mut responses = vec![];
// If the principal def ids match (or are both none), then we're not doing
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
if a_data.principal_def_id() == b_data.principal_def_id() {
if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
responses.push(response);
}
} else if let Some(a_principal) = a_data.principal()
&& let Some(b_principal) = b_data.principal()
{ {
for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) { Ok(Some(b_ty)) if !b_ty.is_ty_var() => b_ty,
if super_trait_ref.def_id() != b_principal.def_id() { Ok(_) => {
continue; return vec![(
} ecx.evaluate_added_goals_and_make_canonical_response(Certainty::AMBIGUOUS)
let erased_trait_ref = super_trait_ref.map_bound(|trait_ref| { .unwrap(),
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref) BuiltinImplSource::Ambiguity,
}); )];
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
responses.push(response);
}
} }
} Err(_) => return vec![],
};
responses let mut results = vec![];
results.extend(
ecx.consider_builtin_dyn_upcast_candidates(goal.param_env, a_ty, b_ty)
.into_iter()
.map(|resp| (resp, BuiltinImplSource::TraitUpcasting)),
);
results.extend(
ecx.consider_builtin_unsize_candidate(goal.param_env, a_ty, b_ty).into_iter().map(
|resp| {
// If we're unsizing from tuple -> tuple, detect
let source =
if matches!((a_ty.kind(), b_ty.kind()), (ty::Tuple(..), ty::Tuple(..)))
{
BuiltinImplSource::TupleUnsize
} else {
BuiltinImplSource::Misc
};
(resp, source)
},
),
);
results
}) })
} }
@ -647,6 +482,214 @@ fn consider_builtin_transmute_candidate(
} }
impl<'tcx> EvalCtxt<'_, 'tcx> { impl<'tcx> EvalCtxt<'_, 'tcx> {
fn consider_builtin_unsize_candidate(
&mut self,
param_env: ty::ParamEnv<'tcx>,
a_ty: Ty<'tcx>,
b_ty: Ty<'tcx>,
) -> QueryResult<'tcx> {
self.probe_candidate("builtin unsize").enter(|ecx| {
let tcx = ecx.tcx();
match (a_ty.kind(), b_ty.kind()) {
(ty::Infer(ty::TyVar(_)), _) | (_, ty::Infer(ty::TyVar(_))) => {
bug!("unexpected type variable in unsize goal")
}
// Trait upcasting, or `dyn Trait + Auto + 'a` -> `dyn Trait + 'b`
(&ty::Dynamic(_, _, ty::Dyn), &ty::Dynamic(_, _, ty::Dyn)) => {
// Dyn upcasting is handled separately, since due to upcasting,
// when there are two supertraits that differ by args, we
// may return more than one query response.
Err(NoSolution)
}
// `T` -> `dyn Trait` unsizing
(_, &ty::Dynamic(data, region, ty::Dyn)) => {
// Can only unsize to an object-safe type
if data
.principal_def_id()
.is_some_and(|def_id| !tcx.check_is_object_safe(def_id))
{
return Err(NoSolution);
}
let Some(sized_def_id) = tcx.lang_items().sized_trait() else {
return Err(NoSolution);
};
// Check that the type implements all of the predicates of the def-id.
// (i.e. the principal, all of the associated types match, and any auto traits)
ecx.add_goals(
data.iter()
.map(|pred| Goal::new(tcx, param_env, pred.with_self_ty(tcx, a_ty))),
);
// The type must be Sized to be unsized.
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::TraitRef::new(tcx, sized_def_id, [a_ty]),
));
// The type must outlive the lifetime of the `dyn` we're unsizing into.
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::Binder::dummy(ty::OutlivesPredicate(a_ty, region)),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// `[T; n]` -> `[T]` unsizing
(&ty::Array(a_elem_ty, ..), &ty::Slice(b_elem_ty)) => {
// We just require that the element type stays the same
ecx.eq(param_env, a_elem_ty, b_elem_ty)?;
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Struct unsizing `Struct<T>` -> `Struct<U>` where `T: Unsize<U>`
(&ty::Adt(a_def, a_args), &ty::Adt(b_def, b_args))
if a_def.is_struct() && a_def.did() == b_def.did() =>
{
let unsizing_params = tcx.unsizing_params_for_adt(a_def.did());
// We must be unsizing some type parameters. This also implies
// that the struct has a tail field.
if unsizing_params.is_empty() {
return Err(NoSolution);
}
let tail_field = a_def.non_enum_variant().tail();
let tail_field_ty = tcx.type_of(tail_field.did);
let a_tail_ty = tail_field_ty.instantiate(tcx, a_args);
let b_tail_ty = tail_field_ty.instantiate(tcx, b_args);
// Substitute just the unsizing params from B into A. The type after
// this substitution must be equal to B. This is so we don't unsize
// unrelated type parameters.
let new_a_args =
tcx.mk_args_from_iter(a_args.iter().enumerate().map(|(i, a)| {
if unsizing_params.contains(i as u32) { b_args[i] } else { a }
}));
let unsized_a_ty = Ty::new_adt(tcx, a_def, new_a_args);
// Finally, we require that `TailA: Unsize<TailB>` for the tail field
// types.
ecx.eq(param_env, unsized_a_ty, b_ty)?;
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::TraitRef::new(
tcx,
tcx.lang_items().unsize_trait().unwrap(),
[a_tail_ty, b_tail_ty],
),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
// Tuple unsizing `(.., T)` -> `(.., U)` where `T: Unsize<U>`
(&ty::Tuple(a_tys), &ty::Tuple(b_tys))
if a_tys.len() == b_tys.len() && !a_tys.is_empty() =>
{
let (a_last_ty, a_rest_tys) = a_tys.split_last().unwrap();
let b_last_ty = b_tys.last().unwrap();
// Substitute just the tail field of B., and require that they're equal.
let unsized_a_ty =
Ty::new_tup_from_iter(tcx, a_rest_tys.iter().chain([b_last_ty]).copied());
ecx.eq(param_env, unsized_a_ty, b_ty)?;
// Similar to ADTs, require that the rest of the fields are equal.
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::TraitRef::new(
tcx,
tcx.lang_items().unsize_trait().unwrap(),
[*a_last_ty, *b_last_ty],
),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
}
_ => Err(NoSolution),
}
})
}
fn consider_builtin_dyn_upcast_candidates(
&mut self,
param_env: ty::ParamEnv<'tcx>,
a_ty: Ty<'tcx>,
b_ty: Ty<'tcx>,
) -> Vec<CanonicalResponse<'tcx>> {
if a_ty.is_ty_var() || b_ty.is_ty_var() {
bug!("unexpected type variable in unsize goal")
}
let ty::Dynamic(a_data, a_region, ty::Dyn) = *a_ty.kind() else {
return vec![];
};
let ty::Dynamic(b_data, b_region, ty::Dyn) = *b_ty.kind() else {
return vec![];
};
let tcx = self.tcx();
// All of a's auto traits need to be in b's auto traits.
let auto_traits_compatible =
b_data.auto_traits().all(|b| a_data.auto_traits().any(|a| a == b));
if !auto_traits_compatible {
return vec![];
}
let mut unsize_dyn_to_principal = |principal: Option<ty::PolyExistentialTraitRef<'tcx>>| {
self.probe_candidate("upcast dyn to principle").enter(|ecx| -> Result<_, NoSolution> {
// Require that all of the trait predicates from A match B, except for
// the auto traits. We do this by constructing a new A type with B's
// auto traits, and equating these types.
let new_a_data = principal
.into_iter()
.map(|trait_ref| trait_ref.map_bound(ty::ExistentialPredicate::Trait))
.chain(a_data.iter().filter(|a| {
matches!(a.skip_binder(), ty::ExistentialPredicate::Projection(_))
}))
.chain(
b_data
.auto_traits()
.map(ty::ExistentialPredicate::AutoTrait)
.map(ty::Binder::dummy),
);
let new_a_data = tcx.mk_poly_existential_predicates_from_iter(new_a_data);
let new_a_ty = Ty::new_dynamic(tcx, new_a_data, b_region, ty::Dyn);
// We also require that A's lifetime outlives B's lifetime.
ecx.eq(param_env, new_a_ty, b_ty)?;
ecx.add_goal(Goal::new(
tcx,
param_env,
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
));
ecx.evaluate_added_goals_and_make_canonical_response(Certainty::Yes)
})
};
let mut responses = vec![];
// If the principal def ids match (or are both none), then we're not doing
// trait upcasting. We're just removing auto traits (or shortening the lifetime).
if a_data.principal_def_id() == b_data.principal_def_id() {
if let Ok(response) = unsize_dyn_to_principal(a_data.principal()) {
responses.push(response);
}
} else if let Some(a_principal) = a_data.principal()
&& let Some(b_principal) = b_data.principal()
{
for super_trait_ref in supertraits(tcx, a_principal.with_self_ty(tcx, a_ty)) {
if super_trait_ref.def_id() != b_principal.def_id() {
continue;
}
let erased_trait_ref = super_trait_ref
.map_bound(|trait_ref| ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref));
if let Ok(response) = unsize_dyn_to_principal(Some(erased_trait_ref)) {
responses.push(response);
}
}
}
responses
}
// Return `Some` if there is an impl (built-in or user provided) that may // Return `Some` if there is an impl (built-in or user provided) that may
// hold for the self type of the goal, which for coherence and soundness // hold for the self type of the goal, which for coherence and soundness
// purposes must disqualify the built-in auto impl assembled by considering // purposes must disqualify the built-in auto impl assembled by considering