mirror of
https://github.com/rust-lang/rust.git
synced 2026-05-29 20:46:07 +03:00
Auto merge of #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
This commit is contained in:
@@ -1,9 +1,10 @@
|
||||
//! Defines the `IntoIter` owned iterator for arrays.
|
||||
|
||||
use crate::clone::TrivialClone;
|
||||
use crate::mem::MaybeUninit;
|
||||
use crate::num::NonZero;
|
||||
use crate::ops::{IndexRange, NeverShortCircuit, Try};
|
||||
use crate::{fmt, iter};
|
||||
use crate::{fmt, iter, ptr};
|
||||
|
||||
#[allow(private_bounds)]
|
||||
trait PartialDrop {
|
||||
@@ -99,9 +100,26 @@ pub(super) const fn empty() -> Self {
|
||||
impl<T: Clone, const N: usize> Clone for PolymorphicIter<[MaybeUninit<T>; N]> {
|
||||
#[inline]
|
||||
fn clone(&self) -> Self {
|
||||
SpecPolymorphicIterClone::<N>::spec_clone(self)
|
||||
}
|
||||
}
|
||||
|
||||
trait SpecPolymorphicIterClone<const N: usize> {
|
||||
fn spec_clone(
|
||||
this: &PolymorphicIter<[MaybeUninit<Self>; N]>,
|
||||
) -> PolymorphicIter<[MaybeUninit<Self>; N]>
|
||||
where
|
||||
Self: Sized;
|
||||
}
|
||||
|
||||
impl<T: Clone, const N: usize> SpecPolymorphicIterClone<N> for T {
|
||||
#[inline]
|
||||
default fn spec_clone(
|
||||
this: &PolymorphicIter<[MaybeUninit<Self>; N]>,
|
||||
) -> PolymorphicIter<[MaybeUninit<Self>; N]> {
|
||||
// Note, we don't really need to match the exact same alive range, so
|
||||
// we can just clone into offset 0 regardless of where `self` is.
|
||||
let mut new = Self::empty();
|
||||
let mut new = PolymorphicIter::<[MaybeUninit<T>; N]>::empty();
|
||||
|
||||
fn clone_into_new<U: Clone>(
|
||||
source: &PolymorphicIter<[MaybeUninit<U>]>,
|
||||
@@ -118,7 +136,33 @@ fn clone_into_new<U: Clone>(
|
||||
}
|
||||
}
|
||||
|
||||
clone_into_new(self, &mut new);
|
||||
clone_into_new(this, &mut new);
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
impl<T: TrivialClone, const N: usize> SpecPolymorphicIterClone<N> for T {
|
||||
#[inline]
|
||||
fn spec_clone(
|
||||
this: &PolymorphicIter<[MaybeUninit<Self>; N]>,
|
||||
) -> PolymorphicIter<[MaybeUninit<Self>; N]> {
|
||||
// Note, we don't really need to match the exact same alive range, so
|
||||
// we can just clone into offset 0 regardless of where `self` is.
|
||||
let mut new = PolymorphicIter::<[MaybeUninit<T>; N]>::empty();
|
||||
|
||||
let len = this.alive.len();
|
||||
|
||||
// SAFETY: These two allocations can not overlap since `new` is allocated
|
||||
// on the stack of this function.
|
||||
unsafe {
|
||||
ptr::copy_nonoverlapping(
|
||||
this.data.as_ptr().add(this.alive.start()),
|
||||
new.data.as_mut_ptr(),
|
||||
len,
|
||||
);
|
||||
new.alive = IndexRange::zero_to(len);
|
||||
}
|
||||
|
||||
new
|
||||
}
|
||||
}
|
||||
|
||||
@@ -106,6 +106,34 @@ fn iterator_clone() {
|
||||
assert_eq!(clone.next_back(), Some(4));
|
||||
assert_eq!(it.next(), Some(2));
|
||||
assert_eq!(clone.next(), Some(2));
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(clone.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn iterator_clone_spec() {
|
||||
#[derive(Debug, PartialEq)]
|
||||
struct Foo(i32);
|
||||
|
||||
impl Clone for Foo {
|
||||
fn clone(&self) -> Self {
|
||||
// nontrivial clone
|
||||
Foo(-self.0)
|
||||
}
|
||||
}
|
||||
|
||||
let mut it = IntoIterator::into_iter([Foo(0), Foo(2), Foo(4), Foo(6), Foo(8)]);
|
||||
assert_eq!(it.next(), Some(Foo(0)));
|
||||
assert_eq!(it.next_back(), Some(Foo(8)));
|
||||
let mut clone = it.clone();
|
||||
assert_eq!(it.next_back(), Some(Foo(6)));
|
||||
assert_eq!(clone.next_back(), Some(Foo(-6)));
|
||||
assert_eq!(it.next_back(), Some(Foo(4)));
|
||||
assert_eq!(clone.next_back(), Some(Foo(-4)));
|
||||
assert_eq!(it.next(), Some(Foo(2)));
|
||||
assert_eq!(clone.next(), Some(Foo(-2)));
|
||||
assert_eq!(it.next(), None);
|
||||
assert_eq!(clone.next(), None);
|
||||
}
|
||||
|
||||
#[test]
|
||||
|
||||
Reference in New Issue
Block a user