|
1 | | -use crate::iter::{TrustedLen, UncheckedIterator}; |
2 | | -use crate::mem::ManuallyDrop; |
3 | | -use crate::ptr::drop_in_place; |
4 | | -use crate::slice; |
| 1 | +use crate::marker::{Destruct, PhantomData}; |
| 2 | +use crate::mem::{ManuallyDrop, SizedTypeProperties, conjure_zst}; |
| 3 | +use crate::ptr::{NonNull, drop_in_place, from_raw_parts_mut, null_mut}; |
5 | 4 |
|
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 | +impl<'l, 'f, T, U, const N: usize, F: FnMut(T) -> U> Drain<'l, 'f, T, N, F> { |
| 6 | + /// This function returns a function that lets you index the given array in const. |
| 7 | + /// As implemented it can optimize better than iterators, and can be constified. |
| 8 | + /// It acts like a sort of guard (owns the array) and iterator combined, which can be implemented |
| 9 | + /// as it is a struct that implements const fn; |
| 10 | + /// in that regard it is somewhat similar to an array::Iter implementing `UncheckedIterator`. |
| 11 | + /// The only method you're really allowed to call is `next()`, |
| 12 | + /// anything else is more or less UB, hence this function being unsafe. |
| 13 | + /// Moved elements will not be dropped. |
| 14 | + /// This will also not actually store the array. |
| 15 | + /// |
| 16 | + /// SAFETY: must only be called `N` times. Thou shalt not drop the array either. |
| 17 | + // FIXME(const-hack): this is a hack for `let guard = Guard(array); |i| f(guard[i])`. |
| 18 | + #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
| 19 | + pub(super) const unsafe fn new(array: &'l mut ManuallyDrop<[T; N]>, f: &'f mut F) -> Self { |
| 20 | + // dont drop the array, transfers "ownership" to Self |
| 21 | + let ptr: NonNull<T> = NonNull::from_mut(array).cast(); |
| 22 | + // SAFETY: |
| 23 | + // Adding `slice.len()` to the starting pointer gives a pointer |
| 24 | + // at the end of `slice`. `end` will never be dereferenced, only checked |
| 25 | + // for direct pointer equality with `ptr` to check if the drainer is done. |
| 26 | + unsafe { |
| 27 | + let end = if T::IS_ZST { null_mut() } else { ptr.as_ptr().add(N) }; |
| 28 | + Self { ptr, end, f, l: PhantomData } |
| 29 | + } |
| 30 | + } |
27 | 31 | } |
28 | 32 |
|
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 | +/// See [`Drain::new`]; this is our fake iterator. |
| 34 | +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
| 35 | +#[unstable(feature = "array_try_map", issue = "79711")] |
| 36 | +pub(super) struct Drain<'l, 'f, T, const N: usize, F> { |
| 37 | + // FIXME(const-hack): This is essentially a slice::IterMut<'static>, replace when possible. |
| 38 | + /// The pointer to the next element to return, or the past-the-end location |
| 39 | + /// if the drainer is empty. |
| 40 | + /// |
| 41 | + /// This address will be used for all ZST elements, never changed. |
| 42 | + /// As we "own" this array, we dont need to store any lifetime. |
| 43 | + ptr: NonNull<T>, |
| 44 | + /// For non-ZSTs, the non-null pointer to the past-the-end element. |
| 45 | + /// For ZSTs, this is null. |
| 46 | + end: *mut T, |
33 | 47 |
|
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()) } |
38 | | - } |
| 48 | + f: &'f mut F, |
| 49 | + l: PhantomData<&'l mut [T; N]>, |
39 | 50 | } |
40 | 51 |
|
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 | | - } |
| 52 | +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
| 53 | +#[unstable(feature = "array_try_map", issue = "79711")] |
| 54 | +impl<T, U, const N: usize, F> const FnOnce<(usize,)> for &mut Drain<'_, '_, T, N, F> |
| 55 | +where |
| 56 | + F: [const] FnMut(T) -> U, |
| 57 | +{ |
| 58 | + type Output = U; |
50 | 59 |
|
51 | | - #[inline] |
52 | | - fn size_hint(&self) -> (usize, Option<usize>) { |
53 | | - let n = self.len(); |
54 | | - (n, Some(n)) |
| 60 | + /// This implementation is useless. |
| 61 | + extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output { |
| 62 | + self.call_mut(args) |
55 | 63 | } |
56 | 64 | } |
57 | | - |
58 | | -impl<T> ExactSizeIterator for Drain<'_, T> { |
59 | | - #[inline] |
60 | | - fn len(&self) -> usize { |
61 | | - self.0.len() |
| 65 | +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
| 66 | +#[unstable(feature = "array_try_map", issue = "79711")] |
| 67 | +impl<T, U, const N: usize, F> const FnMut<(usize,)> for &mut Drain<'_, '_, T, N, F> |
| 68 | +where |
| 69 | + F: [const] FnMut(T) -> U, |
| 70 | +{ |
| 71 | + // FIXME(const-hack): ideally this would be an unsafe fn `next()`, and to use it you would instead `|_| unsafe { drain.next() }`. |
| 72 | + extern "rust-call" fn call_mut( |
| 73 | + &mut self, |
| 74 | + (_ /* ignore argument */,): (usize,), |
| 75 | + ) -> Self::Output { |
| 76 | + if T::IS_ZST { |
| 77 | + // its UB to call this more than N times, so returning more ZSTs is valid. |
| 78 | + // SAFETY: its a ZST? we conjur. |
| 79 | + (self.f)(unsafe { conjure_zst::<T>() }) |
| 80 | + } else { |
| 81 | + // increment before moving; if `f` panics, we drop the rest. |
| 82 | + let p = self.ptr; |
| 83 | + // SAFETY: caller guarantees never called more than N times (see `Drain::new`) |
| 84 | + self.ptr = unsafe { self.ptr.add(1) }; |
| 85 | + // SAFETY: we are allowed to move this. |
| 86 | + (self.f)(unsafe { p.read() }) |
| 87 | + } |
62 | 88 | } |
63 | 89 | } |
| 90 | +#[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
| 91 | +#[unstable(feature = "array_try_map", issue = "79711")] |
| 92 | +impl<T: [const] Destruct, const N: usize, F> const Drop for Drain<'_, '_, T, N, F> { |
| 93 | + fn drop(&mut self) { |
| 94 | + if !T::IS_ZST { |
| 95 | + // SAFETY: we cant read more than N elements |
| 96 | + let slice = unsafe { |
| 97 | + from_raw_parts_mut::<[T]>( |
| 98 | + self.ptr.as_ptr(), |
| 99 | + // SAFETY: `start <= end` |
| 100 | + self.end.offset_from_unsigned(self.ptr.as_ptr()), |
| 101 | + ) |
| 102 | + }; |
64 | 103 |
|
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() } |
| 104 | + // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) |
| 105 | + unsafe { drop_in_place(slice) } |
| 106 | + } |
75 | 107 | } |
76 | 108 | } |
0 commit comments