Skip to content
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.

Commit 1cdcea9

Browse files
committedAug 15, 2019
Auto merge of #62429 - cuviper:iter-closures, r=cramertj
Reduce the genericity of closures in the iterator traits By default, closures inherit the generic parameters of their scope, including `Self`. However, in most cases, the closures used to implement iterators don't need to be generic on the iterator type, only its `Item` type. We can reduce this genericity by redirecting such closures through local functions. This does make the closures more cumbersome to write, but it will hopefully reduce duplication in their monomorphizations, as well as their related type lengths.
2 parents 9e9a136 + bca6f28 commit 1cdcea9

14 files changed

+714
-324
lines changed
 

‎src/libcore/iter/adapters/flatten.rs‎

Lines changed: 52 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -229,16 +229,16 @@ where
229229
if let elt@Some(_) = inner.next() { return elt }
230230
}
231231
match self.iter.next() {
232-
None => return self.backiter.as_mut().and_then(|it| it.next()),
232+
None => return self.backiter.as_mut()?.next(),
233233
Some(inner) => self.frontiter = Some(inner.into_iter()),
234234
}
235235
}
236236
}
237237

238238
#[inline]
239239
fn size_hint(&self) -> (usize, Option<usize>) {
240-
let (flo, fhi) = self.frontiter.as_ref().map_or((0, Some(0)), |it| it.size_hint());
241-
let (blo, bhi) = self.backiter.as_ref().map_or((0, Some(0)), |it| it.size_hint());
240+
let (flo, fhi) = self.frontiter.as_ref().map_or((0, Some(0)), U::size_hint);
241+
let (blo, bhi) = self.backiter.as_ref().map_or((0, Some(0)), U::size_hint);
242242
let lo = flo.saturating_add(blo);
243243
match (self.iter.size_hint(), fhi, bhi) {
244244
((0, Some(0)), Some(a), Some(b)) => (lo, a.checked_add(b)),
@@ -250,20 +250,25 @@ where
250250
fn try_fold<Acc, Fold, R>(&mut self, mut init: Acc, mut fold: Fold) -> R where
251251
Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
252252
{
253+
#[inline]
254+
fn flatten<'a, T: IntoIterator, Acc, R: Try<Ok = Acc>>(
255+
frontiter: &'a mut Option<T::IntoIter>,
256+
fold: &'a mut impl FnMut(Acc, T::Item) -> R,
257+
) -> impl FnMut(Acc, T) -> R + 'a {
258+
move |acc, x| {
259+
let mut mid = x.into_iter();
260+
let r = mid.try_fold(acc, &mut *fold);
261+
*frontiter = Some(mid);
262+
r
263+
}
264+
}
265+
253266
if let Some(ref mut front) = self.frontiter {
254267
init = front.try_fold(init, &mut fold)?;
255268
}
256269
self.frontiter = None;
257270

258-
{
259-
let frontiter = &mut self.frontiter;
260-
init = self.iter.try_fold(init, |acc, x| {
261-
let mut mid = x.into_iter();
262-
let r = mid.try_fold(acc, &mut fold);
263-
*frontiter = Some(mid);
264-
r
265-
})?;
266-
}
271+
init = self.iter.try_fold(init, flatten(&mut self.frontiter, &mut fold))?;
267272
self.frontiter = None;
268273

269274
if let Some(ref mut back) = self.backiter {
@@ -275,13 +280,20 @@ where
275280
}
276281

277282
#[inline]
278-
fn fold<Acc, Fold>(self, init: Acc, mut fold: Fold) -> Acc
283+
fn fold<Acc, Fold>(self, init: Acc, ref mut fold: Fold) -> Acc
279284
where Fold: FnMut(Acc, Self::Item) -> Acc,
280285
{
286+
#[inline]
287+
fn flatten<U: Iterator, Acc>(
288+
fold: &mut impl FnMut(Acc, U::Item) -> Acc,
289+
) -> impl FnMut(Acc, U) -> Acc + '_ {
290+
move |acc, iter| iter.fold(acc, &mut *fold)
291+
}
292+
281293
self.frontiter.into_iter()
282294
.chain(self.iter.map(IntoIterator::into_iter))
283295
.chain(self.backiter)
284-
.fold(init, |acc, iter| iter.fold(acc, &mut fold))
296+
.fold(init, flatten(fold))
285297
}
286298
}
287299

@@ -297,7 +309,7 @@ where
297309
if let elt@Some(_) = inner.next_back() { return elt }
298310
}
299311
match self.iter.next_back() {
300-
None => return self.frontiter.as_mut().and_then(|it| it.next_back()),
312+
None => return self.frontiter.as_mut()?.next_back(),
301313
next => self.backiter = next.map(IntoIterator::into_iter),
302314
}
303315
}
@@ -307,20 +319,27 @@ where
307319
fn try_rfold<Acc, Fold, R>(&mut self, mut init: Acc, mut fold: Fold) -> R where
308320
Self: Sized, Fold: FnMut(Acc, Self::Item) -> R, R: Try<Ok=Acc>
309321
{
310-
if let Some(ref mut back) = self.backiter {
311-
init = back.try_rfold(init, &mut fold)?;
312-
}
313-
self.backiter = None;
314-
322+
#[inline]
323+
fn flatten<'a, T: IntoIterator, Acc, R: Try<Ok = Acc>>(
324+
backiter: &'a mut Option<T::IntoIter>,
325+
fold: &'a mut impl FnMut(Acc, T::Item) -> R,
326+
) -> impl FnMut(Acc, T) -> R + 'a where
327+
T::IntoIter: DoubleEndedIterator,
315328
{
316-
let backiter = &mut self.backiter;
317-
init = self.iter.try_rfold(init, |acc, x| {
329+
move |acc, x| {
318330
let mut mid = x.into_iter();
319-
let r = mid.try_rfold(acc, &mut fold);
331+
let r = mid.try_rfold(acc, &mut *fold);
320332
*backiter = Some(mid);
321333
r
322-
})?;
334+
}
323335
}
336+
337+
if let Some(ref mut back) = self.backiter {
338+
init = back.try_rfold(init, &mut fold)?;
339+
}
340+
self.backiter = None;
341+
342+
init = self.iter.try_rfold(init, flatten(&mut self.backiter, &mut fold))?;
324343
self.backiter = None;
325344

326345
if let Some(ref mut front) = self.frontiter {
@@ -332,12 +351,19 @@ where
332351
}
333352

334353
#[inline]
335-
fn rfold<Acc, Fold>(self, init: Acc, mut fold: Fold) -> Acc
354+
fn rfold<Acc, Fold>(self, init: Acc, ref mut fold: Fold) -> Acc
336355
where Fold: FnMut(Acc, Self::Item) -> Acc,
337356
{
357+
#[inline]
358+
fn flatten<U: DoubleEndedIterator, Acc>(
359+
fold: &mut impl FnMut(Acc, U::Item) -> Acc,
360+
) -> impl FnMut(Acc, U) -> Acc + '_ {
361+
move |acc, iter| iter.rfold(acc, &mut *fold)
362+
}
363+
338364
self.frontiter.into_iter()
339365
.chain(self.iter.map(IntoIterator::into_iter))
340366
.chain(self.backiter)
341-
.rfold(init, |acc, iter| iter.rfold(acc, &mut fold))
367+
.rfold(init, flatten(fold))
342368
}
343369
}

‎src/libcore/iter/adapters/mod.rs‎

Lines changed: 331 additions & 205 deletions
Large diffs are not rendered by default.

‎src/libcore/iter/adapters/zip.rs‎

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -94,11 +94,9 @@ impl<A, B> ZipImpl<A, B> for Zip<A, B>
9494

9595
#[inline]
9696
default fn next(&mut self) -> Option<(A::Item, B::Item)> {
97-
self.a.next().and_then(|x| {
98-
self.b.next().and_then(|y| {
99-
Some((x, y))
100-
})
101-
})
97+
let x = self.a.next()?;
98+
let y = self.b.next()?;
99+
Some((x, y))
102100
}
103101

104102
#[inline]

‎src/libcore/iter/sources.rs‎

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -394,7 +394,8 @@ impl<A, F: FnOnce() -> A> Iterator for OnceWith<F> {
394394

395395
#[inline]
396396
fn next(&mut self) -> Option<A> {
397-
self.gen.take().map(|f| f())
397+
let f = self.gen.take()?;
398+
Some(f())
398399
}
399400

400401
#[inline]
@@ -608,10 +609,9 @@ impl<T, F> Iterator for Successors<T, F>
608609

609610
#[inline]
610611
fn next(&mut self) -> Option<Self::Item> {
611-
self.next.take().map(|item| {
612-
self.next = (self.succ)(&item);
613-
item
614-
})
612+
let item = self.next.take()?;
613+
self.next = (self.succ)(&item);
614+
Some(item)
615615
}
616616

617617
#[inline]

‎src/libcore/iter/traits/accum.rs‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -85,28 +85,28 @@ macro_rules! float_sum_product {
8585
#[stable(feature = "iter_arith_traits", since = "1.12.0")]
8686
impl Sum for $a {
8787
fn sum<I: Iterator<Item=$a>>(iter: I) -> $a {
88-
iter.fold(0.0, |a, b| a + b)
88+
iter.fold(0.0, Add::add)
8989
}
9090
}
9191

9292
#[stable(feature = "iter_arith_traits", since = "1.12.0")]
9393
impl Product for $a {
9494
fn product<I: Iterator<Item=$a>>(iter: I) -> $a {
95-
iter.fold(1.0, |a, b| a * b)
95+
iter.fold(1.0, Mul::mul)
9696
}
9797
}
9898

9999
#[stable(feature = "iter_arith_traits", since = "1.12.0")]
100100
impl<'a> Sum<&'a $a> for $a {
101101
fn sum<I: Iterator<Item=&'a $a>>(iter: I) -> $a {
102-
iter.fold(0.0, |a, b| a + *b)
102+
iter.fold(0.0, Add::add)
103103
}
104104
}
105105

106106
#[stable(feature = "iter_arith_traits", since = "1.12.0")]
107107
impl<'a> Product<&'a $a> for $a {
108108
fn product<I: Iterator<Item=&'a $a>>(iter: I) -> $a {
109-
iter.fold(1.0, |a, b| a * *b)
109+
iter.fold(1.0, Mul::mul)
110110
}
111111
}
112112
)*)

‎src/libcore/iter/traits/double_ended.rs‎

Lines changed: 18 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -219,12 +219,17 @@ pub trait DoubleEndedIterator: Iterator {
219219
/// ```
220220
#[inline]
221221
#[stable(feature = "iter_rfold", since = "1.27.0")]
222-
fn rfold<B, F>(mut self, accum: B, mut f: F) -> B
222+
fn rfold<B, F>(mut self, accum: B, f: F) -> B
223223
where
224224
Self: Sized,
225225
F: FnMut(B, Self::Item) -> B,
226226
{
227-
self.try_rfold(accum, move |acc, x| Ok::<B, !>(f(acc, x))).unwrap()
227+
#[inline]
228+
fn ok<B, T>(mut f: impl FnMut(B, T) -> B) -> impl FnMut(B, T) -> Result<B, !> {
229+
move |acc, x| Ok(f(acc, x))
230+
}
231+
232+
self.try_rfold(accum, ok(f)).unwrap()
228233
}
229234

230235
/// Searches for an element of an iterator from the back that satisfies a predicate.
@@ -271,15 +276,21 @@ pub trait DoubleEndedIterator: Iterator {
271276
/// ```
272277
#[inline]
273278
#[stable(feature = "iter_rfind", since = "1.27.0")]
274-
fn rfind<P>(&mut self, mut predicate: P) -> Option<Self::Item>
279+
fn rfind<P>(&mut self, predicate: P) -> Option<Self::Item>
275280
where
276281
Self: Sized,
277282
P: FnMut(&Self::Item) -> bool
278283
{
279-
self.try_rfold((), move |(), x| {
280-
if predicate(&x) { LoopState::Break(x) }
281-
else { LoopState::Continue(()) }
282-
}).break_value()
284+
#[inline]
285+
fn check<T>(
286+
mut predicate: impl FnMut(&T) -> bool,
287+
) -> impl FnMut((), T) -> LoopState<(), T> {
288+
move |(), x| {
289+
if predicate(&x) { LoopState::Break(x) } else { LoopState::Continue(()) }
290+
}
291+
}
292+
293+
self.try_rfold((), check(predicate)).break_value()
283294
}
284295
}
285296

‎src/libcore/iter/traits/iterator.rs‎

Lines changed: 177 additions & 72 deletions
Large diffs are not rendered by default.
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
//! Check that fold closures aren't duplicated for each iterator type.
2+
// compile-flags: -C opt-level=0
3+
4+
fn main() {
5+
(0i32..10).by_ref().count();
6+
(0i32..=10).by_ref().count();
7+
}
8+
9+
// `count` calls `fold`, which calls `try_fold` -- find the `fold` closure:
10+
// CHECK: {{^define.*Iterator::fold::.*closure}}
11+
//
12+
// Only one closure is needed for both `count` calls, even from different
13+
// monomorphized iterator types, as it's only generic over the item type.
14+
// CHECK-NOT: {{^define.*Iterator::fold::.*closure}}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
//! Check that fold closures aren't generic in the iterator type.
2+
// compile-flags: -C opt-level=0
3+
4+
fn main() {
5+
(0i32..10).by_ref().count();
6+
}
7+
8+
// `count` calls `fold`, which calls `try_fold` -- that `fold` closure should
9+
// not be generic in the iterator type, only in the item type.
10+
// CHECK-NOT: {{^define.*Iterator::fold::.*closure.*Range}}
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
// run-pass
2+
// only-32bit too impatient for 2⁶⁴ items
3+
// ignore-wasm32-bare compiled with panic=abort by default
4+
// compile-flags: -C debug_assertions=yes -C opt-level=3
5+
6+
use std::panic;
7+
use std::usize::MAX;
8+
9+
fn main() {
10+
assert_eq!((0..MAX).by_ref().count(), MAX);
11+
12+
let r = panic::catch_unwind(|| {
13+
(0..=MAX).by_ref().count()
14+
});
15+
assert!(r.is_err());
16+
}
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
// run-pass
2+
// only-32bit too impatient for 2⁶⁴ items
3+
// compile-flags: -C debug_assertions=no -C opt-level=3
4+
5+
use std::panic;
6+
use std::usize::MAX;
7+
8+
fn main() {
9+
assert_eq!((0..MAX).by_ref().count(), MAX);
10+
assert_eq!((0..=MAX).by_ref().count(), 0);
11+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
// run-pass
2+
//! Check that type lengths don't explode with `Map` folds.
3+
//!
4+
//! The normal limit is a million, and this test used to exceed 1.5 million, but
5+
//! now we can survive an even tighter limit. Still seems excessive though...
6+
#![type_length_limit = "256000"]
7+
8+
// Custom wrapper so Iterator methods aren't specialized.
9+
struct Iter<I>(I);
10+
11+
impl<I> Iterator for Iter<I>
12+
where
13+
I: Iterator
14+
{
15+
type Item = I::Item;
16+
17+
fn next(&mut self) -> Option<Self::Item> {
18+
self.0.next()
19+
}
20+
}
21+
22+
fn main() {
23+
let c = Iter(0i32..10)
24+
.map(|x| x)
25+
.map(|x| x)
26+
.map(|x| x)
27+
.map(|x| x)
28+
.map(|x| x)
29+
.map(|x| x)
30+
.map(|x| x)
31+
.map(|x| x)
32+
.map(|x| x)
33+
.map(|x| x)
34+
.map(|x| x)
35+
.map(|x| x)
36+
.count();
37+
assert_eq!(c, 10);
38+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// run-pass
2+
// only-32bit too impatient for 2⁶⁴ items
3+
// ignore-wasm32-bare compiled with panic=abort by default
4+
// compile-flags: -C debug_assertions=yes -C opt-level=3
5+
6+
use std::panic;
7+
use std::usize::MAX;
8+
9+
fn main() {
10+
let n = MAX as u64;
11+
assert_eq!((0..).by_ref().position(|i| i >= n), Some(MAX));
12+
13+
let r = panic::catch_unwind(|| {
14+
(0..).by_ref().position(|i| i > n)
15+
});
16+
assert!(r.is_err());
17+
18+
let r = panic::catch_unwind(|| {
19+
(0..=n + 1).by_ref().position(|_| false)
20+
});
21+
assert!(r.is_err());
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
// run-pass
2+
// only-32bit too impatient for 2⁶⁴ items
3+
// compile-flags: -C debug_assertions=no -C opt-level=3
4+
5+
use std::panic;
6+
use std::usize::MAX;
7+
8+
fn main() {
9+
let n = MAX as u64;
10+
assert_eq!((0..).by_ref().position(|i| i >= n), Some(MAX));
11+
assert_eq!((0..).by_ref().position(|i| i > n), Some(0));
12+
assert_eq!((0..=n + 1).by_ref().position(|_| false), None);
13+
}

0 commit comments

Comments
 (0)
Please sign in to comment.