about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/librustc/middle/traits/mod.rs10
-rw-r--r--src/librustc/middle/traits/project.rs20
-rw-r--r--src/test/run-pass/issue-22036.rs33
3 files changed, 58 insertions, 5 deletions
diff --git a/src/librustc/middle/traits/mod.rs b/src/librustc/middle/traits/mod.rs
index f69bf31626f..bddbb7c02ba 100644
--- a/src/librustc/middle/traits/mod.rs
+++ b/src/librustc/middle/traits/mod.rs
@@ -63,7 +63,7 @@ mod util;
 /// either identifying an `impl` (e.g., `impl Eq for int`) that
 /// provides the required vtable, or else finding a bound that is in
 /// scope. The eventual result is usually a `Selection` (defined below).
-#[derive(Clone)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct Obligation<'tcx, T> {
     pub cause: ObligationCause<'tcx>,
     pub recursion_depth: uint,
@@ -74,7 +74,7 @@ pub type PredicateObligation<'tcx> = Obligation<'tcx, ty::Predicate<'tcx>>;
 pub type TraitObligation<'tcx> = Obligation<'tcx, ty::PolyTraitPredicate<'tcx>>;
 
 /// Why did we incur this obligation? Used for error reporting.
-#[derive(Clone)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct ObligationCause<'tcx> {
     pub span: Span,
 
@@ -89,7 +89,7 @@ pub struct ObligationCause<'tcx> {
     pub code: ObligationCauseCode<'tcx>
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq, Eq)]
 pub enum ObligationCauseCode<'tcx> {
     /// Not well classified or should be obvious from span.
     MiscObligation,
@@ -129,7 +129,7 @@ pub enum ObligationCauseCode<'tcx> {
     CompareImplMethodObligation,
 }
 
-#[derive(Clone)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct DerivedObligationCause<'tcx> {
     /// The trait reference of the parent obligation that led to the
     /// current obligation. Note that only trait obligations lead to
@@ -251,7 +251,7 @@ pub enum Vtable<'tcx, N> {
 /// is `Obligation`, as one might expect. During trans, however, this
 /// is `()`, because trans only requires a shallow resolution of an
 /// impl, and nested obligations are satisfied later.
-#[derive(Clone)]
+#[derive(Clone, PartialEq, Eq)]
 pub struct VtableImplData<'tcx, N> {
     pub impl_def_id: ast::DefId,
     pub substs: subst::Substs<'tcx>,
diff --git a/src/librustc/middle/traits/project.rs b/src/librustc/middle/traits/project.rs
index 9d3ad28e613..7d02adea1fa 100644
--- a/src/librustc/middle/traits/project.rs
+++ b/src/librustc/middle/traits/project.rs
@@ -54,6 +54,7 @@ pub struct MismatchedProjectionTypes<'tcx> {
     pub err: ty::type_err<'tcx>
 }
 
+#[derive(PartialEq, Eq)]
 enum ProjectionTyCandidate<'tcx> {
     ParamEnv(ty::PolyProjectionPredicate<'tcx>),
     Impl(VtableImplData<'tcx, PredicateObligation<'tcx>>),
@@ -481,6 +482,25 @@ fn project_type<'cx,'tcx>(
 
     // We probably need some winnowing logic similar to select here.
 
+    // Drop duplicates.
+    //
+    // Note: `candidates.vec` seems to be on the critical path of the
+    // compiler. Replacing it with an hash set was also tried, which would
+    // render the following dedup unnecessary. It led to cleaner code but
+    // prolonged compiling time of `librustc` from 5m30s to 6m in one test, or
+    // ~9% performance lost.
+    if candidates.vec.len() > 1 {
+        let mut i = 0;
+        while i < candidates.vec.len() {
+            let has_dup = (0..i).any(|j| candidates.vec[i] == candidates.vec[j]);
+            if has_dup {
+                candidates.vec.swap_remove(i);
+            } else {
+                i += 1;
+            }
+        }
+    }
+
     if candidates.ambiguous || candidates.vec.len() > 1 {
         return Err(ProjectionTyError::TooManyCandidates);
     }
diff --git a/src/test/run-pass/issue-22036.rs b/src/test/run-pass/issue-22036.rs
new file mode 100644
index 00000000000..c06a29c09f7
--- /dev/null
+++ b/src/test/run-pass/issue-22036.rs
@@ -0,0 +1,33 @@
+// Copyright 2015 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.
+
+trait DigitCollection: Sized {
+    type Iter: Iterator<Item = u8>;
+    fn digit_iter(self) -> Self::Iter;
+
+    fn digit_sum(self) -> u32 {
+        self.digit_iter()
+            .map(|digit: u8| digit as u32)
+            .fold(0, |sum, digit| sum + digit)
+    }
+}
+
+impl<I> DigitCollection for I where I: Iterator<Item=u8> {
+    type Iter = I;
+
+    fn digit_iter(self) -> I {
+        self
+    }
+}
+
+fn main() {
+    let xs = vec![1u8, 2, 3, 4, 5];
+    assert_eq!(xs.into_iter().digit_sum(), 15);
+}