Skip to content

Commit fb0881f

Browse files
committed
constify from_fn, try_from_fn, try_map, map
1 parent 0415221 commit fb0881f

File tree

4 files changed

+135
-96
lines changed

4 files changed

+135
-96
lines changed

library/core/src/array/drain.rs

Lines changed: 64 additions & 64 deletions
Original file line numberDiff line numberDiff line change
@@ -1,76 +1,76 @@
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
}
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;
2819

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()) }
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: the `i` should also always go up, and musnt skip any, else some things will be leaked.
38+
// SAFETY: if it goes down, we will drop freed elements. not good.
39+
// SAFETY: caller guarantees never called with number >= N (see `Drain::new`)
40+
(self.f)(unsafe { self.array.as_ptr().add(i).read() })
5541
}
5642
}
57-
58-
impl<T> ExactSizeIterator for Drain<'_, T> {
59-
#[inline]
60-
fn len(&self) -> usize {
61-
self.0.len()
43+
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
44+
#[unstable(feature = "array_try_map", issue = "79711")]
45+
impl<T: [const] Destruct, U, const N: usize, F: FnMut(T) -> U> const Drop
46+
for Drain<'_, T, U, N, F>
47+
{
48+
fn drop(&mut self) {
49+
let mut n = self.moved;
50+
while n != N {
51+
// SAFETY: moved must always be < N
52+
unsafe { self.array.as_mut_ptr().add(n).drop_in_place() };
53+
n += 1;
54+
}
6255
}
6356
}
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() }
57+
impl<'a, T, U, const N: usize, F: FnMut(T) -> U> Drain<'a, T, U, N, F> {
58+
/// This function returns a function that lets you index the given array in const.
59+
/// As implemented it can optimize better than iterators, and can be constified.
60+
/// It acts like a sort of guard and iterator combined, which can be implemented
61+
/// as it is a struct that implements const fn;
62+
/// in that regard it is somewhat similar to an array::Iter implementing `UncheckedIterator`.
63+
/// The only method you're really allowed to call is `next()`,
64+
/// anything else is more or less UB, hence this function being unsafe.
65+
/// Moved elements will not be dropped.
66+
///
67+
/// Previously this was implemented as a wrapper around a `slice::Iter`, which
68+
/// called `read()` on the returned `&T`; gnarly stuff.
69+
///
70+
/// SAFETY: must be called in order of 0..N, without indexing out of bounds. (see `Drain::call_mut`)
71+
/// Potentially the function could completely disregard the supplied argument, however i think that behaviour would be unintuitive.
72+
// FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`.
73+
pub(super) const unsafe fn new(array: [T; N], f: &'a mut F) -> Self {
74+
Self { array: ManuallyDrop::new(array), moved: 0, f }
7575
}
7676
}

library/core/src/array/mod.rs

Lines changed: 39 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use crate::error::Error;
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,
@@ -25,7 +26,6 @@ mod drain;
2526
mod equality;
2627
mod iter;
2728

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

@@ -105,9 +105,10 @@ pub fn repeat<T: Clone, const N: usize>(val: T) -> [T; N] {
105105
/// ```
106106
#[inline]
107107
#[stable(feature = "array_from_fn", since = "1.63.0")]
108-
pub fn from_fn<T, const N: usize, F>(f: F) -> [T; N]
108+
#[rustc_const_unstable(feature = "const_array", issue = "147606")]
109+
pub const fn from_fn<T: [const] Destruct, const N: usize, F>(f: F) -> [T; N]
109110
where
110-
F: FnMut(usize) -> T,
111+
F: [const] FnMut(usize) -> T + [const] Destruct,
111112
{
112113
try_from_fn(NeverShortCircuit::wrap_mut_1(f)).0
113114
}
@@ -143,11 +144,15 @@ where
143144
/// ```
144145
#[inline]
145146
#[unstable(feature = "array_try_from_fn", issue = "89379")]
146-
pub fn try_from_fn<R, const N: usize, F>(cb: F) -> ChangeOutputType<R, [R::Output; N]>
147+
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
148+
pub const fn try_from_fn<R, const N: usize>(
149+
cb: impl [const] FnMut(usize) -> R + [const] Destruct,
150+
) -> ChangeOutputType<R, [R::Output; N]>
147151
where
148-
F: FnMut(usize) -> R,
149-
R: Try,
150-
R::Residual: Residual<[R::Output; N]>,
152+
R: [const] Try<
153+
Residual: [const] Residual<[R::Output; N], TryType: [const] Try>,
154+
Output: [const] Destruct,
155+
>,
151156
{
152157
let mut array = [const { MaybeUninit::uninit() }; N];
153158
match try_from_fn_erased(&mut array, cb) {
@@ -549,9 +554,11 @@ impl<T, const N: usize> [T; N] {
549554
/// ```
550555
#[must_use]
551556
#[stable(feature = "array_map", since = "1.55.0")]
552-
pub fn map<F, U>(self, f: F) -> [U; N]
557+
#[rustc_const_unstable(feature = "const_array", issue = "147606")]
558+
pub const fn map<F, U>(self, f: F) -> [U; N]
553559
where
554-
F: FnMut(T) -> U,
560+
F: [const] FnMut(T) -> U + [const] Destruct,
561+
U: [const] Destruct,
555562
{
556563
self.try_map(NeverShortCircuit::wrap_mut_1(f)).0
557564
}
@@ -587,11 +594,22 @@ impl<T, const N: usize> [T; N] {
587594
/// assert_eq!(c, Some(a));
588595
/// ```
589596
#[unstable(feature = "array_try_map", issue = "79711")]
590-
pub fn try_map<R>(self, f: impl FnMut(T) -> R) -> ChangeOutputType<R, [R::Output; N]>
597+
#[rustc_const_unstable(feature = "array_try_map", issue = "79711")]
598+
pub const fn try_map<R>(
599+
self,
600+
mut f: impl [const] FnMut(T) -> R + [const] Destruct,
601+
) -> ChangeOutputType<R, [R::Output; N]>
591602
where
592-
R: Try<Residual: Residual<[R::Output; N]>>,
603+
R: [const] Try<
604+
Residual: [const] Residual<[R::Output; N], TryType: [const] Try>,
605+
Output: [const] Destruct,
606+
>,
593607
{
594-
drain_array_with(self, |iter| try_from_trusted_iterator(iter.map(f)))
608+
// SAFETY: try_from_fn calls `f` with 0..N.
609+
let mut f = unsafe { drain::Drain::new(self, &mut f) };
610+
let out = try_from_fn(&mut f);
611+
mem::forget(f); // it doesnt like being remembered
612+
out
595613
}
596614

597615
/// Returns a slice containing the entire array. Equivalent to `&s[..]`.
@@ -885,13 +903,11 @@ where
885903
/// not optimizing away. So if you give it a shot, make sure to watch what
886904
/// happens in the codegen tests.
887905
#[inline]
888-
fn try_from_fn_erased<T, R>(
889-
buffer: &mut [MaybeUninit<T>],
890-
mut generator: impl FnMut(usize) -> R,
891-
) -> ControlFlow<R::Residual>
892-
where
893-
R: Try<Output = T>,
894-
{
906+
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
907+
const fn try_from_fn_erased<R: [const] Try<Output: [const] Destruct>>(
908+
buffer: &mut [MaybeUninit<R::Output>],
909+
mut generator: impl [const] FnMut(usize) -> R + [const] Destruct,
910+
) -> ControlFlow<R::Residual> {
895911
let mut guard = Guard { array_mut: buffer, initialized: 0 };
896912

897913
while guard.initialized < guard.array_mut.len() {
@@ -930,7 +946,8 @@ impl<T> Guard<'_, T> {
930946
///
931947
/// No more than N elements must be initialized.
932948
#[inline]
933-
pub(crate) unsafe fn push_unchecked(&mut self, item: T) {
949+
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
950+
pub(crate) const unsafe fn push_unchecked(&mut self, item: T) {
934951
// SAFETY: If `initialized` was correct before and the caller does not
935952
// invoke this method more than N times then writes will be in-bounds
936953
// and slots will not be initialized more than once.
@@ -941,11 +958,11 @@ impl<T> Guard<'_, T> {
941958
}
942959
}
943960

944-
impl<T> Drop for Guard<'_, T> {
961+
#[rustc_const_unstable(feature = "array_try_from_fn", issue = "89379")]
962+
impl<T: [const] Destruct> const Drop for Guard<'_, T> {
945963
#[inline]
946964
fn drop(&mut self) {
947965
debug_assert!(self.initialized <= self.array_mut.len());
948-
949966
// SAFETY: this slice will contain only initialized objects.
950967
unsafe {
951968
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.
@@ -396,17 +397,37 @@ pub(crate) type ChangeOutputType<T: Try<Residual: Residual<V>>, V> =
396397
/// Not currently planned to be exposed publicly, so just `pub(crate)`.
397398
#[repr(transparent)]
398399
pub(crate) struct NeverShortCircuit<T>(pub T);
400+
// FIXME(const-hack): replace with `|a| NeverShortCircuit(f(a))` when const closures added.
401+
pub(crate) struct Wrapped<T, A, F: FnMut(A) -> T> {
402+
f: F,
403+
p: PhantomData<(T, A)>,
404+
}
405+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
406+
impl<T, A, F: [const] FnMut(A) -> T + [const] Destruct> const FnOnce<(A,)> for Wrapped<T, A, F> {
407+
type Output = NeverShortCircuit<T>;
408+
409+
extern "rust-call" fn call_once(mut self, args: (A,)) -> Self::Output {
410+
self.call_mut(args)
411+
}
412+
}
413+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
414+
impl<T, A, F: [const] FnMut(A) -> T> const FnMut<(A,)> for Wrapped<T, A, F> {
415+
extern "rust-call" fn call_mut(&mut self, (args,): (A,)) -> Self::Output {
416+
NeverShortCircuit((self.f)(args))
417+
}
418+
}
399419

400420
impl<T> NeverShortCircuit<T> {
401421
/// Wraps a unary function to produce one that wraps the output into a `NeverShortCircuit`.
402422
///
403423
/// This is useful for implementing infallible functions in terms of the `try_` ones,
404424
/// without accidentally capturing extra generic parameters in a closure.
405425
#[inline]
406-
pub(crate) fn wrap_mut_1<A>(
407-
mut f: impl FnMut(A) -> T,
408-
) -> impl FnMut(A) -> NeverShortCircuit<T> {
409-
move |a| NeverShortCircuit(f(a))
426+
pub(crate) const fn wrap_mut_1<A, F>(f: F) -> Wrapped<T, A, F>
427+
where
428+
F: [const] FnMut(A) -> T,
429+
{
430+
Wrapped { f, p: PhantomData }
410431
}
411432

412433
#[inline]
@@ -417,7 +438,8 @@ impl<T> NeverShortCircuit<T> {
417438

418439
pub(crate) enum NeverShortCircuitResidual {}
419440

420-
impl<T> Try for NeverShortCircuit<T> {
441+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
442+
impl<T> const Try for NeverShortCircuit<T> {
421443
type Output = T;
422444
type Residual = NeverShortCircuitResidual;
423445

@@ -431,15 +453,15 @@ impl<T> Try for NeverShortCircuit<T> {
431453
NeverShortCircuit(x)
432454
}
433455
}
434-
435-
impl<T> FromResidual for NeverShortCircuit<T> {
456+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
457+
impl<T> const FromResidual for NeverShortCircuit<T> {
436458
#[inline]
437459
fn from_residual(never: NeverShortCircuitResidual) -> Self {
438460
match never {}
439461
}
440462
}
441-
442-
impl<T> Residual<T> for NeverShortCircuitResidual {
463+
#[rustc_const_unstable(feature = "const_never_short_circuit", issue = "none")]
464+
impl<T: [const] Destruct> const Residual<T> for NeverShortCircuitResidual {
443465
type TryType = NeverShortCircuit<T>;
444466
}
445467

library/coretests/tests/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@
1717
#![feature(char_internals)]
1818
#![feature(char_max_len)]
1919
#![feature(clone_to_uninit)]
20-
#![feature(const_cell_traits)]
2120
#![feature(const_array)]
21+
#![feature(const_cell_traits)]
2222
#![feature(const_cmp)]
2323
#![feature(const_convert)]
2424
#![feature(const_destruct)]

0 commit comments

Comments
 (0)