|
1 | | -use crate::assert_unsafe_precondition; |
2 | | -use crate::marker::Destruct; |
3 | | -use crate::mem::ManuallyDrop; |
| 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}; |
4 | 4 |
|
| 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 | + } |
| 31 | +} |
| 32 | + |
| 33 | +/// See [`Drain::new`]; this is our fake iterator. |
5 | 34 | #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
6 | 35 | #[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, |
| 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, |
| 47 | + |
| 48 | + f: &'f mut F, |
| 49 | + l: PhantomData<&'l mut [T; N]>, |
11 | 50 | } |
| 51 | + |
12 | 52 | #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
13 | 53 | #[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> |
| 54 | +impl<T, U, const N: usize, F> const FnOnce<(usize,)> for &mut Drain<'_, '_, T, N, F> |
15 | 55 | where |
16 | 56 | F: [const] FnMut(T) -> U, |
17 | 57 | { |
18 | 58 | type Output = U; |
19 | 59 |
|
| 60 | + /// This implementation is useless. |
20 | 61 | extern "rust-call" fn call_once(mut self, args: (usize,)) -> Self::Output { |
21 | 62 | self.call_mut(args) |
22 | 63 | } |
23 | 64 | } |
24 | 65 | #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
25 | 66 | #[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> |
| 67 | +impl<T, U, const N: usize, F> const FnMut<(usize,)> for &mut Drain<'_, '_, T, N, F> |
27 | 68 | where |
28 | 69 | F: [const] FnMut(T) -> U, |
29 | 70 | { |
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() }) |
| 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 | + } |
41 | 88 | } |
42 | 89 | } |
43 | 90 | #[rustc_const_unstable(feature = "array_try_map", issue = "79711")] |
44 | 91 | #[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 | | -{ |
| 92 | +impl<T: [const] Destruct, const N: usize, F> const Drop for Drain<'_, '_, T, N, F> { |
48 | 93 | 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; |
| 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, |
| 99 | + // SAFETY: `start <= end` |
| 100 | + self.end.offset_from_unsigned(self.ptr.as_ptr()), |
| 101 | + ) |
| 102 | + }; |
| 103 | + |
| 104 | + // SAFETY: By the type invariant, we're allowed to drop all these. (we own it, after all) |
| 105 | + unsafe { drop_in_place(slice) } |
54 | 106 | } |
55 | 107 | } |
56 | 108 | } |
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 } |
75 | | - } |
76 | | -} |
|
0 commit comments