about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--src/libcore/iter/adapters/mod.rs34
-rw-r--r--src/test/run-pass/iter-map-fold-type-length.rs37
2 files changed, 59 insertions, 12 deletions
diff --git a/src/libcore/iter/adapters/mod.rs b/src/libcore/iter/adapters/mod.rs
index b2702902956..002c52bdac6 100644
--- a/src/libcore/iter/adapters/mod.rs
+++ b/src/libcore/iter/adapters/mod.rs
@@ -594,6 +594,20 @@ impl<I: fmt::Debug, F> fmt::Debug for Map<I, F> {
     }
 }
 
+fn map_fold<T, B, Acc>(
+    mut f: impl FnMut(T) -> B,
+    mut g: impl FnMut(Acc, B) -> Acc,
+) -> impl FnMut(Acc, T) -> Acc {
+    move |acc, elt| g(acc, f(elt))
+}
+
+fn map_try_fold<'a, T, B, Acc, R>(
+    f: &'a mut impl FnMut(T) -> B,
+    mut g: impl FnMut(Acc, B) -> R + 'a,
+) -> impl FnMut(Acc, T) -> R + 'a {
+    move |acc, elt| g(acc, f(elt))
+}
+
 #[stable(feature = "rust1", since = "1.0.0")]
 impl<B, I: Iterator, F> Iterator for Map<I, F> where F: FnMut(I::Item) -> B {
     type Item = B;
@@ -608,18 +622,16 @@ impl<B, I: Iterator, F> Iterator for Map<I, F> where F: FnMut(I::Item) -> B {
         self.iter.size_hint()
     }
 
-    fn try_fold<Acc, G, R>(&mut self, init: Acc, mut g: G) -> R where
+    fn try_fold<Acc, G, R>(&mut self, init: Acc, g: G) -> R where
         Self: Sized, G: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
     {
-        let f = &mut self.f;
-        self.iter.try_fold(init, move |acc, elt| g(acc, f(elt)))
+        self.iter.try_fold(init, map_try_fold(&mut self.f, g))
     }
 
-    fn fold<Acc, G>(self, init: Acc, mut g: G) -> Acc
+    fn fold<Acc, G>(self, init: Acc, g: G) -> Acc
         where G: FnMut(Acc, Self::Item) -> Acc,
     {
-        let mut f = self.f;
-        self.iter.fold(init, move |acc, elt| g(acc, f(elt)))
+        self.iter.fold(init, map_fold(self.f, g))
     }
 }
 
@@ -632,18 +644,16 @@ impl<B, I: DoubleEndedIterator, F> DoubleEndedIterator for Map<I, F> where
         self.iter.next_back().map(&mut self.f)
     }
 
-    fn try_rfold<Acc, G, R>(&mut self, init: Acc, mut g: G) -> R where
+    fn try_rfold<Acc, G, R>(&mut self, init: Acc, g: G) -> R where
         Self: Sized, G: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
     {
-        let f = &mut self.f;
-        self.iter.try_rfold(init, move |acc, elt| g(acc, f(elt)))
+        self.iter.try_rfold(init, map_try_fold(&mut self.f, g))
     }
 
-    fn rfold<Acc, G>(self, init: Acc, mut g: G) -> Acc
+    fn rfold<Acc, G>(self, init: Acc, g: G) -> Acc
         where G: FnMut(Acc, Self::Item) -> Acc,
     {
-        let mut f = self.f;
-        self.iter.rfold(init, move |acc, elt| g(acc, f(elt)))
+        self.iter.rfold(init, map_fold(self.f, g))
     }
 }
 
diff --git a/src/test/run-pass/iter-map-fold-type-length.rs b/src/test/run-pass/iter-map-fold-type-length.rs
new file mode 100644
index 00000000000..b94827f98bb
--- /dev/null
+++ b/src/test/run-pass/iter-map-fold-type-length.rs
@@ -0,0 +1,37 @@
+//! Check that type lengths don't explode with `Map` folds.
+//!
+//! The normal limit is a million, and this test used to exceed 1.5 million, but
+//! now we can survive an even tighter limit. Still seems excessive though...
+#![type_length_limit = "256000"]
+
+// Custom wrapper so Iterator methods aren't specialized.
+struct Iter<I>(I);
+
+impl<I> Iterator for Iter<I>
+where
+    I: Iterator
+{
+    type Item = I::Item;
+
+    fn next(&mut self) -> Option<Self::Item> {
+        self.0.next()
+    }
+}
+
+fn main() {
+    let c = Iter(0i32..10)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .map(|x| x)
+        .count();
+    assert_eq!(c, 10);
+}