From ea1ad792f9c182f18d865d5ff34a86a8ef0e4421 Mon Sep 17 00:00:00 2001
From: Niko Matsakis <niko@alum.mit.edu>
Date: Fri, 2 Jan 2015 11:39:47 -0500
Subject: [PATCH] Evaluate projection predicates during trait selection. Fixes
 #20296.

---
 src/librustc/middle/traits/select.rs          | 46 +++++++++---
 .../associated-types-conditional-dispatch.rs  | 73 +++++++++++++++++++
 2 files changed, 107 insertions(+), 12 deletions(-)
 create mode 100644 src/test/run-pass/associated-types-conditional-dispatch.rs

diff --git a/src/librustc/middle/traits/select.rs b/src/librustc/middle/traits/select.rs
index f9dced088f8..ef8fbdfaa6c 100644
--- a/src/librustc/middle/traits/select.rs
+++ b/src/librustc/middle/traits/select.rs
@@ -289,6 +289,23 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
         }
     }
 
+    fn evaluate_predicates_recursively<'a,'o,I>(&mut self,
+                                                stack: Option<&TraitObligationStack<'o, 'tcx>>,
+                                                mut predicates: I)
+                                                -> EvaluationResult<'tcx>
+        where I : Iterator<&'a PredicateObligation<'tcx>>, 'tcx:'a
+    {
+        let mut result = EvaluatedToOk;
+        for obligation in predicates {
+            match self.evaluate_predicate_recursively(stack, obligation) {
+                EvaluatedToErr(e) => { return EvaluatedToErr(e); }
+                EvaluatedToAmbig => { result = EvaluatedToAmbig; }
+                EvaluatedToOk => { }
+            }
+        }
+        result
+    }
+
     fn evaluate_predicate_recursively<'o>(&mut self,
                                           previous_stack: Option<&TraitObligationStack<'o, 'tcx>>,
                                           obligation: &PredicateObligation<'tcx>)
@@ -320,9 +337,22 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                 EvaluatedToOk
             }
 
-            ty::Predicate::Projection(..) => {
-                // FIXME(#20296) -- we should be able to give a more precise answer here
-                EvaluatedToAmbig
+            ty::Predicate::Projection(ref data) => {
+                let result = self.infcx.probe(|_| {
+                    let project_obligation = obligation.with(data.clone());
+                    project::poly_project_and_unify_type(self, &project_obligation)
+                });
+                match result {
+                    Ok(Some(subobligations)) => {
+                        self.evaluate_predicates_recursively(previous_stack, subobligations.iter())
+                    }
+                    Ok(None) => {
+                        EvaluatedToAmbig
+                    }
+                    Err(_) => {
+                        EvaluatedToErr(Unimplemented)
+                    }
+                }
             }
         }
     }
@@ -1026,15 +1056,7 @@ impl<'cx, 'tcx> SelectionContext<'cx, 'tcx> {
                             selection: Selection<'tcx>)
                             -> EvaluationResult<'tcx>
     {
-        let mut result = EvaluatedToOk;
-        for obligation in selection.iter_nested() {
-            match self.evaluate_predicate_recursively(stack, obligation) {
-                EvaluatedToErr(e) => { return EvaluatedToErr(e); }
-                EvaluatedToAmbig => { result = EvaluatedToAmbig; }
-                EvaluatedToOk => { }
-            }
-        }
-        result
+        self.evaluate_predicates_recursively(stack, selection.iter_nested())
     }
 
     /// Returns true if `candidate_i` should be dropped in favor of `candidate_j`.
diff --git a/src/test/run-pass/associated-types-conditional-dispatch.rs b/src/test/run-pass/associated-types-conditional-dispatch.rs
new file mode 100644
index 00000000000..4efd0ef348d
--- /dev/null
+++ b/src/test/run-pass/associated-types-conditional-dispatch.rs
@@ -0,0 +1,73 @@
+// Copyright 2014 The Rust Project Developers. See the COPYRIGHT
+// file at the top-level directory of this distribution and at
+// http://rust-lang.org/COPYRIGHT.
+//
+// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// Test that we evaluate projection predicates to winnow out
+// candidates during trait selection and method resolution (#20296).
+// If we don't properly winnow out candidates based on the output type
+// `Output=[A]`, then the impl marked with `(*)` is seen to conflict
+// with all the others.
+
+#![feature(associated_types, default_type_params)]
+
+use std::ops::Deref;
+
+pub trait MyEq<Sized? U=Self> for Sized? {
+    fn eq(&self, u: &U) -> bool;
+}
+
+impl<A, B> MyEq<[B]> for [A]
+    where A : MyEq<B>
+{
+    fn eq(&self, other: &[B]) -> bool {
+        self.len() == other.len() &&
+            self.iter().zip(other.iter())
+                       .all(|(a, b)| MyEq::eq(a, b))
+    }
+}
+
+// (*) This impl conflicts with everything unless the `Output=[A]`
+// constraint is considered.
+impl<'a, A, B, Lhs> MyEq<[B; 0]> for Lhs
+    where A: MyEq<B>, Lhs: Deref<Output=[A]>
+{
+    fn eq(&self, other: &[B; 0]) -> bool {
+        MyEq::eq(&**self, other.as_slice())
+    }
+}
+
+struct DerefWithHelper<H, T> {
+    pub helper: H
+}
+
+trait Helper<T> {
+    fn helper_borrow(&self) -> &T;
+}
+
+impl<T> Helper<T> for Option<T> {
+    fn helper_borrow(&self) -> &T {
+        self.as_ref().unwrap()
+    }
+}
+
+impl<T, H: Helper<T>> Deref for DerefWithHelper<H, T> {
+    type Output = T;
+
+    fn deref(&self) -> &T {
+        self.helper.helper_borrow()
+    }
+}
+
+pub fn check<T: MyEq>(x: T, y: T) -> bool {
+    let d: DerefWithHelper<Option<T>, T> = DerefWithHelper { helper: Some(x) };
+    d.eq(&y)
+}
+
+pub fn main() {
+}