Skip to content

Commit d1504e0

Browse files
committed
constify from_fn, try_from_fn, try_map, map
1 parent 7e0b9ed commit d1504e0

File tree

3 files changed

+116
-92
lines changed

3 files changed

+116
-92
lines changed

library/core/src/array/drain.rs

Lines changed: 50 additions & 65 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,61 @@
1-
use crate::iter::{TrustedLen, UncheckedIterator};
1+
use crate::assert_unsafe_precondition;
2+
use crate::marker::Destruct;
23
use crate::mem::ManuallyDrop;
3-
use crate::ptr::drop_in_place;
4-
use crate::slice;
54

6-
/// A situationally-optimized version of `array.into_iter().for_each(func)`.
7-
///
8-
/// [`crate::array::IntoIter`]s are great when you need an owned iterator, but
9-
/// storing the entire array *inside* the iterator like that can sometimes
10-
/// pessimize code. Notable, it can be more bytes than you really want to move
11-
/// around, and because the array accesses index into it SRoA has a harder time
12-
/// optimizing away the type than it does iterators that just hold a couple pointers.
13-
///
14-
/// Thus this function exists, which gives a way to get *moved* access to the
15-
/// elements of an array using a small iterator -- no bigger than a slice iterator.
16-
///
17-
/// The function-taking-a-closure structure makes it safe, as it keeps callers
18-
/// from looking at already-dropped elements.
19-
pub(crate) fn drain_array_with<T, R, const N: usize>(
20-
array: [T; N],
21-
func: impl for<'a> FnOnce(Drain<'a, T>) -> R,
22-
) -> R {
23-
let mut array = ManuallyDrop::new(array);
24-
// SAFETY: Now that the local won't drop it, it's ok to construct the `Drain` which will.
25-
let drain = Drain(array.iter_mut());
26-
func(drain)
5+
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
6+
#[unstable(feature = "array_try_map", issue = "79711")]
7+
pub(super) struct Drain<'a, T, U, const N: usize, F: FnMut(T) -> U> {
8+
array: ManuallyDrop<[T; N]>,
9+
moved: usize,
10+
f: &'a mut F,
2711
}
28-
29-
/// See [`drain_array_with`] -- this is `pub(crate)` only so it's allowed to be
30-
/// mentioned in the signature of that method. (Otherwise it hits `E0446`.)
31-
// INVARIANT: It's ok to drop the remainder of the inner iterator.
32-
pub(crate) struct Drain<'a, T>(slice::IterMut<'a, T>);
33-
34-
impl<T> Drop for Drain<'_, T> {
35-
fn drop(&mut self) {
36-
// SAFETY: By the type invariant, we're allowed to drop all these.
37-
unsafe { drop_in_place(self.0.as_mut_slice()) }
12+
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
13+
#[unstable(feature = "array_try_map", issue = "79711")]
14+
impl<T, U, const N: usize, F> const FnOnce<(usize,)> for &mut Drain<'_, T, U, N, F>
15+
where
16+
F: [const] FnMut(T) -> U,
17+
{
18+
type Output = U;
19+
20+
extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output {
21+
self.call_mut(args)
3822
}
3923
}
40-
41-
impl<T> Iterator for Drain<'_, T> {
42-
type Item = T;
43-
44-
#[inline]
45-
fn next(&mut self) -> Option<T> {
46-
let p: *const T = self.0.next()?;
47-
// SAFETY: The iterator was already advanced, so we won't drop this later.
48-
Some(unsafe { p.read() })
49-
}
50-
51-
#[inline]
52-
fn size_hint(&self) -> (usize, Option<usize>) {
53-
let n = self.len();
54-
(n, Some(n))
24+
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
25+
#[unstable(feature = "array_try_map", issue = "79711")]
26+
impl<T, U, const N: usize, F> const FnMut<(usize,)> for &mut Drain<'_, T, U, N, F>
27+
where
28+
F: [const] FnMut(T) -> U,
29+
{
30+
extern "rust-call" fn call_mut(&mut self, (i,): (usize,)) -> Self::Output {
31+
// SAFETY: increment moved before moving. if `f` panics, we drop the rest.
32+
self.moved += 1;
33+
assert_unsafe_precondition!(
34+
check_library_ub,
35+
"musnt index array out of bounds", (i: usize = i, size: usize = N) => i < size
36+
);
37+
// SAFETY: caller guarantees never called with number >= N (see `Drain::new`)
38+
(self.f)(unsafe { self.array.as_ptr().add(i).read() })
5539
}
5640
}
57-
58-
impl<T> ExactSizeIterator for Drain<'_, T> {
59-
#[inline]
60-
fn len(&self) -> usize {
61-
self.0.len()
41+
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
42+
#[unstable(feature = "array_try_map", issue = "79711")]
43+
impl<T: [const] Destruct, U, const N: usize, F: FnMut(T) -> U> const Drop
44+
for Drain<'_, T, U, N, F>
45+
{
46+
fn drop(&mut self) {
47+
let mut n = self.moved;
48+
while n != N {
49+
// SAFETY: moved must always be < N
50+
unsafe { self.array.as_mut_ptr().add(n).drop_in_place() };
51+
n += 1;
52+
}
6253
}
6354
}
64-
65-
// SAFETY: This is a 1:1 wrapper for a slice iterator, which is also `TrustedLen`.
66-
unsafe impl<T> TrustedLen for Drain<'_, T> {}
67-
68-
impl<T> UncheckedIterator for Drain<'_, T> {
69-
unsafe fn next_unchecked(&mut self) -> T {
70-
// SAFETY: `Drain` is 1:1 with the inner iterator, so if the caller promised
71-
// that there's an element left, the inner iterator has one too.
72-
let p: *const T = unsafe { self.0.next_unchecked() };
73-
// SAFETY: The iterator was already advanced, so we won't drop this later.
74-
unsafe { p.read() }
55+
impl<'a, T, U, const N: usize, F: FnMut(T) -> U> Drain<'a, T, U, N, F> {
56+
/// SAFETY: must be called without indexing out of bounds. (see `Drain::call_mut`)
57+
// FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`.
58+
pub(super) const unsafe fn new(array: [T; N], f: &'a mut F) -> Self {
59+
Self { array: ManuallyDrop::new(array), moved: 0, f }
7560
}
7661
}

library/core/src/array/mod.rs

Lines changed: 35 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::fmt;
1212
use crate::hash::{self, Hash};
1313
use crate::intrinsics::transmute_unchecked;
1414
use crate::iter::{UncheckedIterator, repeat_n};
15+
use crate::marker::Destruct;
1516
use crate::mem::{self, MaybeUninit};
1617
use crate::ops::{
1718
ChangeOutputType, ControlFlow, FromResidual, Index, IndexMut, NeverShortCircuit, Residual, Try,
@@ -24,7 +25,6 @@ mod drain;
2425
mod equality;
2526
mod iter;
2627

27-
pub(crate) use drain::drain_array_with;
2828
#[stable(feature = "array_value_iter", since = "1.51.0")]
2929
pub use iter::IntoIter;
3030

@@ -104,9 +104,10 @@ pub fn repeat<T: Clone, const N: usize>(val: T) -> [T; N] {
104104
/// ```
105105
#[inline]
106106
#[stable(feature = "array_from_fn", since = "1.63.0")]
107-
pub fn from_fn<T, const N: usize, F>(f: F) -> [T; N]
107+
#[rustc_const_unstable(feature = "const_array", issue = "147606")]
108+
pub const fn from_fn<T: [const] Destruct, const N: usize, F>(f: F) -> [T; N]
108109
where
109-
F: FnMut(usize) -> T,
110+
F: [const] FnMut(usize) -> T + [const] Destruct,
110111
{
111112
try_from_fn(NeverShortCircuit::wrap_mut_1(f)).0
112113
}
@@ -142,11 +143,13 @@ where
142143
/// ```
143144
#[inline]
144145
#[unstable(feature = "array_try_from_fn", issue = "89379")]
145-
pub fn try_from_fn<R, const N: usize, F>(cb: F) -> ChangeOutputType<R, [R::Output; N]>
146+
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
147+
pub const fn try_from_fn<R, const N: usize, F>(cb: F) -> ChangeOutputType<R, [R::Output; N]>
146148
where
147-
F: FnMut(usize) -> R,
148-
R: Try,
149-
R::Residual: Residual<[R::Output; N]>,
149+
F: [const] FnMut(usize) -> R + [const] Destruct,
150+
R: [const] Try<Residual: Residual<[R::Output; N]>>,
151+
R::Output: [const] Destruct,
152+
<R::Residual as Residual<[R::Output; N]>>::TryType: [const] Try,
150153
{
151154
let mut array = [const { MaybeUninit::uninit() }; N];
152155
match try_from_fn_erased(&mut array, cb) {
@@ -542,9 +545,11 @@ impl<T, const N: usize> [T; N] {
542545
/// ```
543546
#[must_use]
544547
#[stable(feature = "array_map", since = "1.55.0")]
545-
pub fn map<F, U>(self, f: F) -> [U; N]
548+
#[rustc_const_unstable(feature = "const_array", issue = "147606")]
549+
pub const fn map<F, U>(self, f: F) -> [U; N]
546550
where
547-
F: FnMut(T) -> U,
551+
F: [const] FnMut(T) -> U + [const] Destruct,
552+
U: [const] Destruct,
548553
{
549554
self.try_map(NeverShortCircuit::wrap_mut_1(f)).0
550555
}
@@ -580,11 +585,19 @@ impl<T, const N: usize> [T; N] {
580585
/// assert_eq!(c, Some(a));
581586
/// ```
582587
#[unstable(feature = "array_try_map", issue = "79711")]
583-
pub fn try_map<R>(self, f: impl FnMut(T) -> R) -> ChangeOutputType<R, [R::Output; N]>
588+
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
589+
pub const fn try_map<R, F>(self, mut f: F) -> ChangeOutputType<R, [R::Output; N]>
584590
where
585-
R: Try<Residual: Residual<[R::Output; N]>>,
591+
F: [const] FnMut(T) -> R + [const] Destruct,
592+
R: [const] Try<Residual: Residual<[R::Output; N]>>,
593+
R::Output: [const] Destruct,
594+
<R::Residual as Residual<[R::Output; N]>>::TryType: [const] Try,
586595
{
587-
drain_array_with(self, |iter| try_from_trusted_iterator(iter.map(f)))
596+
// SAFETY: try_from_fn calls `f` with 0..N.
597+
let mut f = unsafe { drain::Drain::new(self, &mut f) };
598+
let out = try_from_fn(&mut f);
599+
mem::forget(f); // it doesnt like being remembered
600+
out
588601
}
589602

590603
/// Returns a slice containing the entire array. Equivalent to `&s[..]`.
@@ -878,12 +891,15 @@ where
878891
/// not optimizing away. So if you give it a shot, make sure to watch what
879892
/// happens in the codegen tests.
880893
#[inline]
881-
fn try_from_fn_erased<T, R>(
894+
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
895+
const fn try_from_fn_erased<T, R, F>(
882896
buffer: &mut [MaybeUninit<T>],
883-
mut generator: impl FnMut(usize) -> R,
897+
mut generator: F,
884898
) -> ControlFlow<R::Residual>
885899
where
886-
R: Try<Output = T>,
900+
T: [const] Destruct,
901+
R: [const] Try<Output = T>,
902+
F: [const] FnMut(usize) -> R + [const] Destruct,
887903
{
888904
let mut guard = Guard { array_mut: buffer, initialized: 0 };
889905

@@ -923,7 +939,8 @@ impl<T> Guard<'_, T> {
923939
///
924940
/// No more than N elements must be initialized.
925941
#[inline]
926-
pub(crate) unsafe fn push_unchecked(&mut self, item: T) {
942+
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
943+
pub(crate) const unsafe fn push_unchecked(&mut self, item: T) {
927944
// SAFETY: If `initialized` was correct before and the caller does not
928945
// invoke this method more than N times then writes will be in-bounds
929946
// and slots will not be initialized more than once.
@@ -934,11 +951,11 @@ impl<T> Guard<'_, T> {
934951
}
935952
}
936953

937-
impl<T> Drop for Guard<'_, T> {
954+
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
955+
impl<T: [const] Destruct> const Drop for Guard<'_, T> {
938956
#[inline]
939957
fn drop(&mut self) {
940958
debug_assert!(self.initialized <= self.array_mut.len());
941-
942959
// SAFETY: this slice will contain only initialized objects.
943960
unsafe {
944961
self.array_mut.get_unchecked_mut(..self.initialized).assume_init_drop();

library/core/src/ops/try_trait.rs

Lines changed: 31 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
use crate::marker::{Destruct, PhantomData};
12
use crate::ops::ControlFlow;
23

34
/// The `?` operator and `try {}` blocks.
@@ -380,17 +381,37 @@ pub(crate) type ChangeOutputType<T: Try<Residual: Residual<V>>, V> =
380381
/// Not currently planned to be exposed publicly, so just `pub(crate)`.
381382
#[repr(transparent)]
382383
pub(crate) struct NeverShortCircuit<T>(pub T);
384+
// FIXME(const-hack): replace with `|a| NeverShortCircuit(f(a))` when const closures added.
385+
pub(crate) struct Wrapped<T, A, F: FnMut(A) -> T> {
386+
f: F,
387+
p: PhantomData<(T, A)>,
388+
}
389+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
390+
impl<T, A, F: [const] FnMut(A) -> T + [const] Destruct> const FnOnce<(A,)> for Wrapped<T, A, F> {
391+
type Output = NeverShortCircuit<T>;
392+
393+
extern "rust-call" fn call_once(mut self, args: (A,)) -> Self::Output {
394+
self.call_mut(args)
395+
}
396+
}
397+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
398+
impl<T, A, F: [const] FnMut(A) -> T> const FnMut<(A,)> for Wrapped<T, A, F> {
399+
extern "rust-call" fn call_mut(&mut self, (args,): (A,)) -> Self::Output {
400+
NeverShortCircuit((self.f)(args))
401+
}
402+
}
383403

384404
impl<T> NeverShortCircuit<T> {
385405
/// Wraps a unary function to produce one that wraps the output into a `NeverShortCircuit`.
386406
///
387407
/// This is useful for implementing infallible functions in terms of the `try_` ones,
388408
/// without accidentally capturing extra generic parameters in a closure.
389409
#[inline]
390-
pub(crate) fn wrap_mut_1<A>(
391-
mut f: impl FnMut(A) -> T,
392-
) -> impl FnMut(A) -> NeverShortCircuit<T> {
393-
move |a| NeverShortCircuit(f(a))
410+
pub(crate) const fn wrap_mut_1<A, F>(f: F) -> Wrapped<T, A, F>
411+
where
412+
F: [const] FnMut(A) -> T,
413+
{
414+
Wrapped { f, p: PhantomData }
394415
}
395416

396417
#[inline]
@@ -401,7 +422,8 @@ impl<T> NeverShortCircuit<T> {
401422

402423
pub(crate) enum NeverShortCircuitResidual {}
403424

404-
impl<T> Try for NeverShortCircuit<T> {
425+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
426+
impl<T> const Try for NeverShortCircuit<T> {
405427
type Output = T;
406428
type Residual = NeverShortCircuitResidual;
407429

@@ -415,15 +437,15 @@ impl<T> Try for NeverShortCircuit<T> {
415437
NeverShortCircuit(x)
416438
}
417439
}
418-
419-
impl<T> FromResidual for NeverShortCircuit<T> {
440+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
441+
impl<T> const FromResidual for NeverShortCircuit<T> {
420442
#[inline]
421443
fn from_residual(never: NeverShortCircuitResidual) -> Self {
422444
match never {}
423445
}
424446
}
425-
426-
impl<T> Residual<T> for NeverShortCircuitResidual {
447+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
448+
impl<T> const Residual<T> for NeverShortCircuitResidual {
427449
type TryType = NeverShortCircuit<T>;
428450
}
429451

0 commit comments

Comments
 (0)