diff options
6 files changed, 82 insertions, 37 deletions
diff --git a/compiler/rustc_middle/src/query/mod.rs b/compiler/rustc_middle/src/query/mod.rs index 240f2c0792c..fc4c343372a 100644 --- a/compiler/rustc_middle/src/query/mod.rs +++ b/compiler/rustc_middle/src/query/mod.rs @@ -157,7 +157,7 @@ rustc_queries! { } /// Returns the list of bounds that can be used for - /// `SelectionCandidate::ProjectionCandidate` and + /// `SelectionCandidate::ProjectionCandidate(_)` and /// `ProjectionTyCandidate::TraitDef`. /// Specifically this is the bounds written on the trait's type /// definition, or those after the `impl` keyword diff --git a/compiler/rustc_middle/src/traits/select.rs b/compiler/rustc_middle/src/traits/select.rs index 6ad514c6be2..358ead507b4 100644 --- a/compiler/rustc_middle/src/traits/select.rs +++ b/compiler/rustc_middle/src/traits/select.rs @@ -105,9 +105,10 @@ pub enum SelectionCandidate<'tcx> { ImplCandidate(DefId), AutoImplCandidate(DefId), - /// This is a trait matching with a projected type as `Self`, and - /// we found an applicable bound in the trait definition. - ProjectionCandidate, + /// This is a trait matching with a projected type as `Self`, and we found + /// an applicable bound in the trait definition. The `usize` is an index + /// into the list returned by `tcx.item_bounds`. + ProjectionCandidate(usize), /// Implementation of a `Fn`-family trait by one of the anonymous types /// generated for a `||` expression. diff --git a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs index 6c7732f28e4..1ac9be64f1f 100644 --- a/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs +++ b/compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs @@ -323,12 +323,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { _ => return, } - let result = self.infcx.probe(|_| { - self.match_projection_obligation_against_definition_bounds(obligation).is_some() - }); + let result = self + .infcx + .probe(|_| self.match_projection_obligation_against_definition_bounds(obligation)); - if result { - candidates.vec.push(ProjectionCandidate); + for predicate_index in result { + candidates.vec.push(ProjectionCandidate(predicate_index)); } } diff --git a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs index ced99b3eb64..d0b4bec1b1a 100644 --- a/compiler/rustc_trait_selection/src/traits/select/confirmation.rs +++ b/compiler/rustc_trait_selection/src/traits/select/confirmation.rs @@ -70,8 +70,8 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { Ok(ImplSource::AutoImpl(data)) } - ProjectionCandidate => { - let obligations = self.confirm_projection_candidate(obligation); + ProjectionCandidate(idx) => { + let obligations = self.confirm_projection_candidate(obligation, idx); Ok(ImplSource::Param(obligations)) } @@ -121,11 +121,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { fn confirm_projection_candidate( &mut self, obligation: &TraitObligation<'tcx>, + idx: usize, ) -> Vec<PredicateObligation<'tcx>> { self.infcx.commit_unconditionally(|_| { - let candidate = self - .match_projection_obligation_against_definition_bounds(obligation) - .unwrap_or_else(|| bug!("Can't find selected projection candidate")); + let tcx = self.tcx(); + + 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 .infcx .at(&obligation.cause, obligation.param_env) @@ -139,7 +150,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { ); }); // 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( self, obligation.param_env, @@ -152,7 +163,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { obligation.cause.clone(), obligation.recursion_depth + 1, obligation.param_env, - ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(self.tcx()), + ty::PredicateKind::WellFormed(self_ty.into()).to_predicate(tcx), )); obligations }) diff --git a/compiler/rustc_trait_selection/src/traits/select/mod.rs b/compiler/rustc_trait_selection/src/traits/select/mod.rs index 0537e94cc1c..e1dd3f215ca 100644 --- a/compiler/rustc_trait_selection/src/traits/select/mod.rs +++ b/compiler/rustc_trait_selection/src/traits/select/mod.rs @@ -1163,11 +1163,12 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { /// /// 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 - /// `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( &mut self, obligation: &TraitObligation<'tcx>, - ) -> Option<ty::PolyTraitRef<'tcx>> { + ) -> smallvec::SmallVec<[usize; 2]> { let poly_trait_predicate = self.infcx().resolve_vars_if_possible(&obligation.predicate); let placeholder_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 matching_bound = bounds.iter().find_map(|bound| { - 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) + let matching_bounds = bounds + .iter() + .enumerate() + .filter_map(|(idx, bound)| { + 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() - }) { - return Some(bound); + }) { + return Some(idx); + } } - } - None - }); + None + }) + .collect(); debug!( "match_projection_obligation_against_definition_bounds: \ - matching_bound={:?}", - matching_bound + matching_bounds={:?}", + matching_bounds ); - matching_bound + matching_bounds } fn match_projection( @@ -1299,14 +1308,14 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { // clause so don't go around looking for impls. !is_global(cand) } - ObjectCandidate | ProjectionCandidate => { + ObjectCandidate | ProjectionCandidate(_) => { // Arbitrarily give param candidates priority // over projection and object candidates. !is_global(cand) } ParamCandidate(..) => false, }, - ObjectCandidate | ProjectionCandidate => match victim.candidate { + ObjectCandidate | ProjectionCandidate(_) => match victim.candidate { AutoImplCandidate(..) => { bug!( "default implementations shouldn't be recorded \ @@ -1323,10 +1332,11 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> { | BuiltinUnsizeCandidate | BuiltinCandidate { .. } | TraitAliasCandidate(..) => true, - ObjectCandidate | ProjectionCandidate => { - // Arbitrarily give param candidates priority - // over projection and object candidates. - true + ObjectCandidate | ProjectionCandidate(_) => { + // Shouldn't have both an object and projection candidate, + // nor multiple object candidates. Multiple projection + // candidates are ambiguous. + false } ParamCandidate(ref cand) => is_global(cand), }, diff --git a/src/test/ui/associated-types/associated-types-bound-ambiguity.rs b/src/test/ui/associated-types/associated-types-bound-ambiguity.rs new file mode 100644 index 00000000000..9f179b6454e --- /dev/null +++ b/src/test/ui/associated-types/associated-types-bound-ambiguity.rs @@ -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() {} |
