Don't be incomplete
This commit is contained in:
parent
238beae5e5
commit
7c942ccb0c
3 changed files with 128 additions and 92 deletions
|
@ -745,7 +745,10 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
|
|
||||||
match (source.kind(), target.kind()) {
|
match (source.kind(), target.kind()) {
|
||||||
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
|
// Trait+Kx+'a -> Trait+Ky+'b (upcasts).
|
||||||
(&ty::Dynamic(ref data_a, _, ty::Dyn), &ty::Dynamic(ref data_b, _, ty::Dyn)) => {
|
(
|
||||||
|
&ty::Dynamic(ref a_data, a_region, ty::Dyn),
|
||||||
|
&ty::Dynamic(ref b_data, b_region, ty::Dyn),
|
||||||
|
) => {
|
||||||
// Upcast coercions permit several things:
|
// Upcast coercions permit several things:
|
||||||
//
|
//
|
||||||
// 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
|
// 1. Dropping auto traits, e.g., `Foo + Send` to `Foo`
|
||||||
|
@ -757,19 +760,19 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
//
|
//
|
||||||
// We always perform upcasting coercions when we can because of reason
|
// We always perform upcasting coercions when we can because of reason
|
||||||
// #2 (region bounds).
|
// #2 (region bounds).
|
||||||
let auto_traits_compatible = data_b
|
let auto_traits_compatible = b_data
|
||||||
.auto_traits()
|
.auto_traits()
|
||||||
// All of a's auto traits need to be in b's auto traits.
|
// All of a's auto traits need to be in b's auto traits.
|
||||||
.all(|b| data_a.auto_traits().any(|a| a == b));
|
.all(|b| a_data.auto_traits().any(|a| a == b));
|
||||||
if auto_traits_compatible {
|
if auto_traits_compatible {
|
||||||
let principal_def_id_a = data_a.principal_def_id();
|
let principal_def_id_a = a_data.principal_def_id();
|
||||||
let principal_def_id_b = data_b.principal_def_id();
|
let principal_def_id_b = b_data.principal_def_id();
|
||||||
if principal_def_id_a == principal_def_id_b {
|
if principal_def_id_a == principal_def_id_b {
|
||||||
// no cyclic
|
// no cyclic
|
||||||
candidates.vec.push(BuiltinUnsizeCandidate);
|
candidates.vec.push(BuiltinUnsizeCandidate);
|
||||||
} else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
|
} else if principal_def_id_a.is_some() && principal_def_id_b.is_some() {
|
||||||
// not casual unsizing, now check whether this is trait upcasting coercion.
|
// not casual unsizing, now check whether this is trait upcasting coercion.
|
||||||
let principal_a = data_a.principal().unwrap();
|
let principal_a = a_data.principal().unwrap();
|
||||||
let target_trait_did = principal_def_id_b.unwrap();
|
let target_trait_did = principal_def_id_b.unwrap();
|
||||||
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
|
let source_trait_ref = principal_a.with_self_ty(self.tcx(), source);
|
||||||
if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object(
|
if let Some(deref_trait_ref) = self.need_migrate_deref_output_trait_object(
|
||||||
|
@ -785,9 +788,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
for (idx, upcast_trait_ref) in
|
for (idx, upcast_trait_ref) in
|
||||||
util::supertraits(self.tcx(), source_trait_ref).enumerate()
|
util::supertraits(self.tcx(), source_trait_ref).enumerate()
|
||||||
{
|
{
|
||||||
if upcast_trait_ref.def_id() == target_trait_did {
|
self.infcx.probe(|_| {
|
||||||
candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
|
if upcast_trait_ref.def_id() == target_trait_did
|
||||||
}
|
&& let Ok(nested) = self.match_upcast_principal(
|
||||||
|
obligation,
|
||||||
|
upcast_trait_ref,
|
||||||
|
a_data,
|
||||||
|
b_data,
|
||||||
|
a_region,
|
||||||
|
b_region,
|
||||||
|
)
|
||||||
|
{
|
||||||
|
if nested.is_none() {
|
||||||
|
candidates.ambiguous = true;
|
||||||
|
}
|
||||||
|
candidates.vec.push(TraitUpcastingUnsizeCandidate(idx));
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -890,89 +890,16 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
let unnormalized_upcast_principal =
|
let unnormalized_upcast_principal =
|
||||||
util::supertraits(tcx, source_principal).nth(idx).unwrap();
|
util::supertraits(tcx, source_principal).nth(idx).unwrap();
|
||||||
|
|
||||||
let mut nested = vec![];
|
let nested = self
|
||||||
let upcast_principal = normalize_with_depth_to(
|
.match_upcast_principal(
|
||||||
self,
|
obligation,
|
||||||
obligation.param_env,
|
unnormalized_upcast_principal,
|
||||||
obligation.cause.clone(),
|
a_data,
|
||||||
obligation.recursion_depth + 1,
|
b_data,
|
||||||
unnormalized_upcast_principal,
|
a_region,
|
||||||
&mut nested,
|
b_region,
|
||||||
);
|
)?
|
||||||
|
.expect("did not expect ambiguity during confirmation");
|
||||||
for bound in b_data {
|
|
||||||
match bound.skip_binder() {
|
|
||||||
// Check that a's supertrait (upcast_principal) is compatible
|
|
||||||
// with the target (b_ty).
|
|
||||||
ty::ExistentialPredicate::Trait(target_principal) => {
|
|
||||||
nested.extend(
|
|
||||||
self.infcx
|
|
||||||
.at(&obligation.cause, obligation.param_env)
|
|
||||||
.sup(
|
|
||||||
DefineOpaqueTypes::No,
|
|
||||||
upcast_principal.map_bound(|trait_ref| {
|
|
||||||
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
|
||||||
}),
|
|
||||||
bound.rebind(target_principal),
|
|
||||||
)
|
|
||||||
.map_err(|_| SelectionError::Unimplemented)?
|
|
||||||
.into_obligations(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Check that b_ty's projection is satisfied by exactly one of
|
|
||||||
// a_ty's projections. First, we look through the list to see if
|
|
||||||
// any match. If not, error. Then, if *more* than one matches, we
|
|
||||||
// return ambiguity. Otherwise, if exactly one matches, equate
|
|
||||||
// it with b_ty's projection.
|
|
||||||
ty::ExistentialPredicate::Projection(target_projection) => {
|
|
||||||
let target_projection = bound.rebind(target_projection);
|
|
||||||
let mut matching_projections =
|
|
||||||
a_data.projection_bounds().filter(|source_projection| {
|
|
||||||
// Eager normalization means that we can just use can_eq
|
|
||||||
// here instead of equating and processing obligations.
|
|
||||||
source_projection.item_def_id() == target_projection.item_def_id()
|
|
||||||
&& self.infcx.can_eq(
|
|
||||||
obligation.param_env,
|
|
||||||
*source_projection,
|
|
||||||
target_projection,
|
|
||||||
)
|
|
||||||
});
|
|
||||||
let Some(source_projection) = matching_projections.next() else {
|
|
||||||
return Err(SelectionError::Unimplemented);
|
|
||||||
};
|
|
||||||
if matching_projections.next().is_some() {
|
|
||||||
// This is incomplete but I don't care. We should never
|
|
||||||
// have more than one projection that ever applies with
|
|
||||||
// eager norm and actually implementable traits, since
|
|
||||||
// you can't have two supertraits like:
|
|
||||||
// `trait A: B<i32, Assoc = First> + B<i32, Assoc = Second>`
|
|
||||||
return Err(SelectionError::Unimplemented);
|
|
||||||
}
|
|
||||||
nested.extend(
|
|
||||||
self.infcx
|
|
||||||
.at(&obligation.cause, obligation.param_env)
|
|
||||||
.sup(DefineOpaqueTypes::No, source_projection, target_projection)
|
|
||||||
.map_err(|_| SelectionError::Unimplemented)?
|
|
||||||
.into_obligations(),
|
|
||||||
);
|
|
||||||
}
|
|
||||||
// Check that b_ty's auto trait is present in a_ty's bounds.
|
|
||||||
ty::ExistentialPredicate::AutoTrait(def_id) => {
|
|
||||||
if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
|
|
||||||
return Err(SelectionError::Unimplemented);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Also require that a_ty's lifetime outlives b_ty's lifetime.
|
|
||||||
nested.push(Obligation::with_depth(
|
|
||||||
tcx,
|
|
||||||
obligation.cause.clone(),
|
|
||||||
obligation.recursion_depth + 1,
|
|
||||||
obligation.param_env,
|
|
||||||
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
|
|
||||||
));
|
|
||||||
|
|
||||||
let vtable_segment_callback = {
|
let vtable_segment_callback = {
|
||||||
let mut vptr_offset = 0;
|
let mut vptr_offset = 0;
|
||||||
|
|
|
@ -2477,6 +2477,98 @@ impl<'tcx> SelectionContext<'_, 'tcx> {
|
||||||
Ok(Normalized { value: impl_args, obligations: nested_obligations })
|
Ok(Normalized { value: impl_args, obligations: nested_obligations })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn match_upcast_principal(
|
||||||
|
&mut self,
|
||||||
|
obligation: &PolyTraitObligation<'tcx>,
|
||||||
|
unnormalized_upcast_principal: ty::PolyTraitRef<'tcx>,
|
||||||
|
a_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||||
|
b_data: &'tcx ty::List<ty::PolyExistentialPredicate<'tcx>>,
|
||||||
|
a_region: ty::Region<'tcx>,
|
||||||
|
b_region: ty::Region<'tcx>,
|
||||||
|
) -> SelectionResult<'tcx, Vec<PredicateObligation<'tcx>>> {
|
||||||
|
let tcx = self.tcx();
|
||||||
|
let mut nested = vec![];
|
||||||
|
|
||||||
|
let upcast_principal = normalize_with_depth_to(
|
||||||
|
self,
|
||||||
|
obligation.param_env,
|
||||||
|
obligation.cause.clone(),
|
||||||
|
obligation.recursion_depth + 1,
|
||||||
|
unnormalized_upcast_principal,
|
||||||
|
&mut nested,
|
||||||
|
);
|
||||||
|
|
||||||
|
for bound in b_data {
|
||||||
|
match bound.skip_binder() {
|
||||||
|
// Check that a_ty's supertrait (upcast_principal) is compatible
|
||||||
|
// with the target (b_ty).
|
||||||
|
ty::ExistentialPredicate::Trait(target_principal) => {
|
||||||
|
nested.extend(
|
||||||
|
self.infcx
|
||||||
|
.at(&obligation.cause, obligation.param_env)
|
||||||
|
.sup(
|
||||||
|
DefineOpaqueTypes::No,
|
||||||
|
upcast_principal.map_bound(|trait_ref| {
|
||||||
|
ty::ExistentialTraitRef::erase_self_ty(tcx, trait_ref)
|
||||||
|
}),
|
||||||
|
bound.rebind(target_principal),
|
||||||
|
)
|
||||||
|
.map_err(|_| SelectionError::Unimplemented)?
|
||||||
|
.into_obligations(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Check that b_ty's projection is satisfied by exactly one of
|
||||||
|
// a_ty's projections. First, we look through the list to see if
|
||||||
|
// any match. If not, error. Then, if *more* than one matches, we
|
||||||
|
// return ambiguity. Otherwise, if exactly one matches, equate
|
||||||
|
// it with b_ty's projection.
|
||||||
|
ty::ExistentialPredicate::Projection(target_projection) => {
|
||||||
|
let target_projection = bound.rebind(target_projection);
|
||||||
|
let mut matching_projections =
|
||||||
|
a_data.projection_bounds().filter(|source_projection| {
|
||||||
|
// Eager normalization means that we can just use can_eq
|
||||||
|
// here instead of equating and processing obligations.
|
||||||
|
source_projection.item_def_id() == target_projection.item_def_id()
|
||||||
|
&& self.infcx.can_eq(
|
||||||
|
obligation.param_env,
|
||||||
|
*source_projection,
|
||||||
|
target_projection,
|
||||||
|
)
|
||||||
|
});
|
||||||
|
let Some(source_projection) = matching_projections.next() else {
|
||||||
|
return Err(SelectionError::Unimplemented);
|
||||||
|
};
|
||||||
|
if matching_projections.next().is_some() {
|
||||||
|
return Ok(None);
|
||||||
|
}
|
||||||
|
nested.extend(
|
||||||
|
self.infcx
|
||||||
|
.at(&obligation.cause, obligation.param_env)
|
||||||
|
.sup(DefineOpaqueTypes::No, source_projection, target_projection)
|
||||||
|
.map_err(|_| SelectionError::Unimplemented)?
|
||||||
|
.into_obligations(),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
// Check that b_ty's auto traits are present in a_ty's bounds.
|
||||||
|
ty::ExistentialPredicate::AutoTrait(def_id) => {
|
||||||
|
if !a_data.auto_traits().any(|source_def_id| source_def_id == def_id) {
|
||||||
|
return Err(SelectionError::Unimplemented);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
nested.push(Obligation::with_depth(
|
||||||
|
tcx,
|
||||||
|
obligation.cause.clone(),
|
||||||
|
obligation.recursion_depth + 1,
|
||||||
|
obligation.param_env,
|
||||||
|
ty::Binder::dummy(ty::OutlivesPredicate(a_region, b_region)),
|
||||||
|
));
|
||||||
|
|
||||||
|
Ok(Some(nested))
|
||||||
|
}
|
||||||
|
|
||||||
/// Normalize `where_clause_trait_ref` and try to match it against
|
/// Normalize `where_clause_trait_ref` and try to match it against
|
||||||
/// `obligation`. If successful, return any predicates that
|
/// `obligation`. If successful, return any predicates that
|
||||||
/// result from the normalization.
|
/// result from the normalization.
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue