Skip to content

Commit dc375db

Browse files
committed
Auto merge of rust-lang#156634 - Paladynee:lib/array-intoiter-spec-clone, r=joboet
lib: specialize Clone of array IntoIter This PR adds the `TrivialClone` specialization to the `PolymorphicIter` type, which `array::IntoIter` uses. I also added another coretest for the default case (already existing test catches the specialized one). There are no unwinding concerns since `TrivialClone` means cloning = copying and we never call `Clone::clone`. `array:IntoIter` is being used on a lot of places in both `core` and in user code, and `array::IntoIter` (nor does its users) override `Clone` often and just delegate to the inner type `PolymorphicIter` which does not yet do the `TrivialClone` specialization and is burdened by the iterator and unwind safety shenanigans for all types, so this PR should address some of that. This should in turn slightly reduce both stack usage and remove unwinding prologue/epilogue etc. from such functions and improve our `-Cno-prepopulate-passes` codegen too. perf: https://godbolt.org/z/eTPjj3hP3
2 parents cced03b + 5cec957 commit dc375db

2 files changed

Lines changed: 75 additions & 3 deletions

File tree

library/core/src/array/iter/iter_inner.rs

Lines changed: 47 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
//! Defines the `IntoIter` owned iterator for arrays.
22
3+
use crate::clone::TrivialClone;
34
use crate::mem::MaybeUninit;
45
use crate::num::NonZero;
56
use crate::ops::{IndexRange, NeverShortCircuit, Try};
6-
use crate::{fmt, iter};
7+
use crate::{fmt, iter, ptr};
78

89
#[allow(private_bounds)]
910
trait PartialDrop {
@@ -99,9 +100,26 @@ impl<T, const N: usize> PolymorphicIter<[MaybeUninit<T>; N]> {
99100
impl<T: Clone, const N: usize> Clone for PolymorphicIter<[MaybeUninit<T>; N]> {
100101
#[inline]
101102
fn clone(&self) -> Self {
103+
SpecPolymorphicIterClone::<N>::spec_clone(self)
104+
}
105+
}
106+
107+
trait SpecPolymorphicIterClone<const N: usize> {
108+
fn spec_clone(
109+
this: &PolymorphicIter<[MaybeUninit<Self>; N]>,
110+
) -> PolymorphicIter<[MaybeUninit<Self>; N]>
111+
where
112+
Self: Sized;
113+
}
114+
115+
impl<T: Clone, const N: usize> SpecPolymorphicIterClone<N> for T {
116+
#[inline]
117+
default fn spec_clone(
118+
this: &PolymorphicIter<[MaybeUninit<Self>; N]>,
119+
) -> PolymorphicIter<[MaybeUninit<Self>; N]> {
102120
// Note, we don't really need to match the exact same alive range, so
103121
// we can just clone into offset 0 regardless of where `self` is.
104-
let mut new = Self::empty();
122+
let mut new = PolymorphicIter::<[MaybeUninit<T>; N]>::empty();
105123

106124
fn clone_into_new<U: Clone>(
107125
source: &PolymorphicIter<[MaybeUninit<U>]>,
@@ -118,7 +136,33 @@ impl<T: Clone, const N: usize> Clone for PolymorphicIter<[MaybeUninit<T>; N]> {
118136
}
119137
}
120138

121-
clone_into_new(self, &mut new);
139+
clone_into_new(this, &mut new);
140+
new
141+
}
142+
}
143+
144+
impl<T: TrivialClone, const N: usize> SpecPolymorphicIterClone<N> for T {
145+
#[inline]
146+
fn spec_clone(
147+
this: &PolymorphicIter<[MaybeUninit<Self>; N]>,
148+
) -> PolymorphicIter<[MaybeUninit<Self>; N]> {
149+
// Note, we don't really need to match the exact same alive range, so
150+
// we can just clone into offset 0 regardless of where `self` is.
151+
let mut new = PolymorphicIter::<[MaybeUninit<T>; N]>::empty();
152+
153+
let len = this.alive.len();
154+
155+
// SAFETY: These two allocations can not overlap since `new` is allocated
156+
// on the stack of this function.
157+
unsafe {
158+
ptr::copy_nonoverlapping(
159+
this.data.as_ptr().add(this.alive.start()),
160+
new.data.as_mut_ptr(),
161+
len,
162+
);
163+
new.alive = IndexRange::zero_to(len);
164+
}
165+
122166
new
123167
}
124168
}

library/coretests/tests/array.rs

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,34 @@ fn iterator_clone() {
106106
assert_eq!(clone.next_back(), Some(4));
107107
assert_eq!(it.next(), Some(2));
108108
assert_eq!(clone.next(), Some(2));
109+
assert_eq!(it.next(), None);
110+
assert_eq!(clone.next(), None);
111+
}
112+
113+
#[test]
114+
fn iterator_clone_spec() {
115+
#[derive(Debug, PartialEq)]
116+
struct Foo(i32);
117+
118+
impl Clone for Foo {
119+
fn clone(&self) -> Self {
120+
// nontrivial clone
121+
Foo(-self.0)
122+
}
123+
}
124+
125+
let mut it = IntoIterator::into_iter([Foo(0), Foo(2), Foo(4), Foo(6), Foo(8)]);
126+
assert_eq!(it.next(), Some(Foo(0)));
127+
assert_eq!(it.next_back(), Some(Foo(8)));
128+
let mut clone = it.clone();
129+
assert_eq!(it.next_back(), Some(Foo(6)));
130+
assert_eq!(clone.next_back(), Some(Foo(-6)));
131+
assert_eq!(it.next_back(), Some(Foo(4)));
132+
assert_eq!(clone.next_back(), Some(Foo(-4)));
133+
assert_eq!(it.next(), Some(Foo(2)));
134+
assert_eq!(clone.next(), Some(Foo(-2)));
135+
assert_eq!(it.next(), None);
136+
assert_eq!(clone.next(), None);
109137
}
110138

111139
#[test]

0 commit comments

Comments
 (0)