Handle multiple applicable projection candidates
This commit is contained in:
parent
bc08b791bc
commit
cfee49593d
6 changed files with 82 additions and 37 deletions
|
@ -157,7 +157,7 @@ rustc_queries! {
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the list of bounds that can be used for
|
/// Returns the list of bounds that can be used for
|
||||||
/// `SelectionCandidate::ProjectionCandidate` and
|
/// `SelectionCandidate::ProjectionCandidate(_)` and
|
||||||
/// `ProjectionTyCandidate::TraitDef`.
|
/// `ProjectionTyCandidate::TraitDef`.
|
||||||
/// Specifically this is the bounds written on the trait's type
|
/// Specifically this is the bounds written on the trait's type
|
||||||
/// definition, or those after the `impl` keyword
|
/// definition, or those after the `impl` keyword
|
||||||
|
|
|
@ -105,9 +105,10 @@ pub enum SelectionCandidate<'tcx> {
|
||||||
ImplCandidate(DefId),
|
ImplCandidate(DefId),
|
||||||
AutoImplCandidate(DefId),
|
AutoImplCandidate(DefId),
|
||||||
|
|
||||||
/// This is a trait matching with a projected type as `Self`, and
|
/// This is a trait matching with a projected type as `Self`, and we found
|
||||||
/// we found an applicable bound in the trait definition.
|
/// an applicable bound in the trait definition. The `usize` is an index
|
||||||
ProjectionCandidate,
|
/// into the list returned by `tcx.item_bounds`.
|
||||||
|
ProjectionCandidate(usize),
|
||||||
|
|
||||||
/// Implementation of a `Fn`-family trait by one of the anonymous types
|
/// Implementation of a `Fn`-family trait by one of the anonymous types
|
||||||
/// generated for a `||` expression.
|
/// generated for a `||` expression.
|
||||||
|
|
|
@ -323,12 +323,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
_ => return,
|
_ => return,
|
||||||
}
|
}
|
||||||
|
|
||||||
let result = self.infcx.probe(|_| {
|
let result = self
|
||||||
self.match_projection_obligation_against_definition_bounds(obligation).is_some()
|
.infcx
|
||||||
});
|
.probe(|_| self.match_projection_obligation_against_definition_bounds(obligation));
|
||||||
|
|
||||||
if result {
|
for predicate_index in result {
|
||||||
candidates.vec.push(ProjectionCandidate);
|
candidates.vec.push(ProjectionCandidate(predicate_index));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -70,8 +70,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
Ok(ImplSource::AutoImpl(data))
|
Ok(ImplSource::AutoImpl(data))
|
||||||
}
|
}
|
||||||
|
|
||||||
ProjectionCandidate => {
|
ProjectionCandidate(idx) => {
|
||||||
let obligations = self.confirm_projection_candidate(obligation);
|
let obligations = self.confirm_projection_candidate(obligation, idx);
|
||||||
Ok(ImplSource::Param(obligations))
|
Ok(ImplSource::Param(obligations))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -121,11 +121,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
fn confirm_projection_candidate(
|
fn confirm_projection_candidate(
|
||||||
&mut self,
|
&mut self,
|
||||||
obligation: &TraitObligation<'tcx>,
|
obligation: &TraitObligation<'tcx>,
|
||||||
|
idx: usize,
|
||||||
) -> Vec<PredicateObligation<'tcx>> {
|
) -> Vec<PredicateObligation<'tcx>> {
|
||||||
self.infcx.commit_unconditionally(|_| {
|
self.infcx.commit_unconditionally(|_| {
|
||||||
let candidate = self
|
let tcx = self.tcx();
|
||||||
.match_projection_obligation_against_definition_bounds(obligation)
|
|
||||||
.unwrap_or_else(|| bug!("Can't find selected projection candidate"));
|
let bound_self_ty = self.infcx.shallow_resolve(obligation.self_ty());
|
||||||
|
let (def_id, substs) = match bound_self_ty.skip_binder().kind {
|
||||||
|
ty::Projection(proj) => (proj.item_def_id, proj.substs),
|
||||||
|
ty::Opaque(def_id, substs) => (def_id, substs),
|
||||||
|
_ => bug!("projection candidate for unexpected type: {:?}", bound_self_ty),
|
||||||
|
};
|
||||||
|
|
||||||
|
let candidate_predicate = tcx.item_bounds(def_id)[idx].subst(tcx, substs);
|
||||||
|
let candidate = candidate_predicate
|
||||||
|
.to_opt_poly_trait_ref()
|
||||||
|
.expect("projection candidate is not a trait predicate");
|
||||||
let mut obligations = self
|
let mut obligations = self
|
||||||
.infcx
|
.infcx
|
||||||
.at(&obligation.cause, obligation.param_env)
|
.at(&obligation.cause, obligation.param_env)
|
||||||
|
@ -139,7 +150,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
);
|
);
|
||||||
});
|
});
|
||||||
// Require that the projection is well-formed.
|
// Require that the projection is well-formed.
|
||||||
let self_ty = self.infcx.replace_bound_vars_with_placeholders(&obligation.self_ty());
|
let self_ty = self.infcx.replace_bound_vars_with_placeholders(&bound_self_ty);
|
||||||
let self_ty = normalize_with_depth_to(
|
let self_ty = normalize_with_depth_to(
|
||||||
self,
|
self,
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
|
@ -152,7 +163,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
obligation.cause.clone(),
|
obligation.cause.clone(),
|
||||||
obligation.recursion_depth + 1,
|
obligation.recursion_depth + 1,
|
||||||
obligation.param_env,
|
obligation.param_env,
|
||||||
ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(self.tcx()),
|
ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(tcx),
|
||||||
));
|
));
|
||||||
obligations
|
obligations
|
||||||
})
|
})
|
||||||
|
|
|
@ -1163,11 +1163,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
///
|
///
|
||||||
/// Given an obligation like `<T as Foo>::Bar: Baz` where the self type is
|
/// Given an obligation like `<T as Foo>::Bar: Baz` where the self type is
|
||||||
/// a projection, look at the bounds of `T::Bar`, see if we can find a
|
/// a projection, look at the bounds of `T::Bar`, see if we can find a
|
||||||
/// `Baz` bound and it there is one it returns it.
|
/// `Baz` bound. We return indexes into the list returned by
|
||||||
|
/// `tcx.item_bounds` for any applicable bounds.
|
||||||
fn match_projection_obligation_against_definition_bounds(
|
fn match_projection_obligation_against_definition_bounds(
|
||||||
&mut self,
|
&mut self,
|
||||||
obligation: &TraitObligation<'tcx>,
|
obligation: &TraitObligation<'tcx>,
|
||||||
) -> Option<ty::PolyTraitRef<'tcx>> {
|
) -> smallvec::SmallVec<[usize; 2]> {
|
||||||
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
|
let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate);
|
||||||
let placeholder_trait_predicate =
|
let placeholder_trait_predicate =
|
||||||
self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
|
self.infcx().replace_bound_vars_with_placeholders(&poly_trait_predicate);
|
||||||
|
@ -1192,25 +1193,33 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
};
|
};
|
||||||
let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
|
let bounds = tcx.item_bounds(def_id).subst(tcx, substs);
|
||||||
|
|
||||||
let matching_bound = bounds.iter().find_map(|bound| {
|
let matching_bounds = bounds
|
||||||
if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
|
.iter()
|
||||||
let bound = ty::Binder::bind(pred.trait_ref);
|
.enumerate()
|
||||||
if self.infcx.probe(|_| {
|
.filter_map(|(idx, bound)| {
|
||||||
self.match_projection(obligation, bound, placeholder_trait_predicate.trait_ref)
|
if let ty::PredicateAtom::Trait(pred, _) = bound.skip_binders() {
|
||||||
|
let bound = ty::Binder::bind(pred.trait_ref);
|
||||||
|
if self.infcx.probe(|_| {
|
||||||
|
self.match_projection(
|
||||||
|
obligation,
|
||||||
|
bound,
|
||||||
|
placeholder_trait_predicate.trait_ref,
|
||||||
|
)
|
||||||
.is_ok()
|
.is_ok()
|
||||||
}) {
|
}) {
|
||||||
return Some(bound);
|
return Some(idx);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
None
|
||||||
None
|
})
|
||||||
});
|
.collect();
|
||||||
|
|
||||||
debug!(
|
debug!(
|
||||||
"match_projection_obligation_against_definition_bounds: \
|
"match_projection_obligation_against_definition_bounds: \
|
||||||
matching_bound={:?}",
|
matching_bounds={:?}",
|
||||||
matching_bound
|
matching_bounds
|
||||||
);
|
);
|
||||||
matching_bound
|
matching_bounds
|
||||||
}
|
}
|
||||||
|
|
||||||
fn match_projection(
|
fn match_projection(
|
||||||
|
@ -1299,14 +1308,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
// clause so don't go around looking for impls.
|
// clause so don't go around looking for impls.
|
||||||
!is_global(cand)
|
!is_global(cand)
|
||||||
}
|
}
|
||||||
ObjectCandidate | ProjectionCandidate => {
|
ObjectCandidate | ProjectionCandidate(_) => {
|
||||||
// Arbitrarily give param candidates priority
|
// Arbitrarily give param candidates priority
|
||||||
// over projection and object candidates.
|
// over projection and object candidates.
|
||||||
!is_global(cand)
|
!is_global(cand)
|
||||||
}
|
}
|
||||||
ParamCandidate(..) => false,
|
ParamCandidate(..) => false,
|
||||||
},
|
},
|
||||||
ObjectCandidate | ProjectionCandidate => match victim.candidate {
|
ObjectCandidate | ProjectionCandidate(_) => match victim.candidate {
|
||||||
AutoImplCandidate(..) => {
|
AutoImplCandidate(..) => {
|
||||||
bug!(
|
bug!(
|
||||||
"default implementations shouldn't be recorded \
|
"default implementations shouldn't be recorded \
|
||||||
|
@ -1323,10 +1332,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
|
||||||
| BuiltinUnsizeCandidate
|
| BuiltinUnsizeCandidate
|
||||||
| BuiltinCandidate { .. }
|
| BuiltinCandidate { .. }
|
||||||
| TraitAliasCandidate(..) => true,
|
| TraitAliasCandidate(..) => true,
|
||||||
ObjectCandidate | ProjectionCandidate => {
|
ObjectCandidate | ProjectionCandidate(_) => {
|
||||||
// Arbitrarily give param candidates priority
|
// Shouldn't have both an object and projection candidate,
|
||||||
// over projection and object candidates.
|
// nor multiple object candidates. Multiple projection
|
||||||
true
|
// candidates are ambiguous.
|
||||||
|
false
|
||||||
}
|
}
|
||||||
ParamCandidate(ref cand) => is_global(cand),
|
ParamCandidate(ref cand) => is_global(cand),
|
||||||
},
|
},
|
||||||
|
|
|
@ -0,0 +1,23 @@
|
||||||
|
// Make sure that if there are multiple applicable bounds on a projection, we
|
||||||
|
// consider them ambiguous. In this test we are initially trying to solve
|
||||||
|
// `Self::Repr: From<_>`, which is ambiguous until we later infer `_` to
|
||||||
|
// `{integer}`.
|
||||||
|
|
||||||
|
// check-pass
|
||||||
|
|
||||||
|
trait PrimeField: Sized {
|
||||||
|
type Repr: From<u64> + From<Self>;
|
||||||
|
type Repr2: From<Self> + From<u64>;
|
||||||
|
|
||||||
|
fn method() {
|
||||||
|
Self::Repr::from(10);
|
||||||
|
Self::Repr2::from(10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn function<T: PrimeField>() {
|
||||||
|
T::Repr::from(10);
|
||||||
|
T::Repr2::from(10);
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {}
|
Loading…
Add table
Add a link
Reference in a new issue