about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_middle/src/query/mod.rs2
-rw-r--r--compiler/rustc_middle/src/traits/select.rs7
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/candidate_assembly.rs10
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/confirmation.rs25
-rw-r--r--compiler/rustc_trait_selection/src/traits/select/mod.rs52
-rw-r--r--src/test/ui/associated-types/associated-types-bound-ambiguity.rs23
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() {}