redo the drain · rust-lang/rust@e3a2c23 (original) (raw)
1
``
`-
use crate::assert_unsafe_precondition;
`
2
``
`-
use crate:📑:Destruct;
`
3
``
`-
use crate::mem::ManuallyDrop;
`
``
1
`+
use crate:📑:{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 = 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,
`
``
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::() })
`
``
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.as_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
``
`-
}
`