]>
Commit | Line | Data |
---|---|---|
49aad941 | 1 | use rustc_index::{Idx, IndexVec}; |
9ffffee4 | 2 | use std::{mem, rc::Rc, sync::Arc}; |
fc512014 | 3 | |
a2a8927a | 4 | pub trait IdFunctor: Sized { |
fc512014 XL |
5 | type Inner; |
6 | ||
a2a8927a | 7 | fn try_map_id<F, E>(self, f: F) -> Result<Self, E> |
fc512014 | 8 | where |
a2a8927a | 9 | F: FnMut(Self::Inner) -> Result<Self::Inner, E>; |
fc512014 XL |
10 | } |
11 | ||
12 | impl<T> IdFunctor for Box<T> { | |
13 | type Inner = T; | |
14 | ||
15 | #[inline] | |
a2a8927a | 16 | fn try_map_id<F, E>(self, mut f: F) -> Result<Self, E> |
fc512014 | 17 | where |
a2a8927a | 18 | F: FnMut(Self::Inner) -> Result<Self::Inner, E>, |
fc512014 XL |
19 | { |
20 | let raw = Box::into_raw(self); | |
a2a8927a | 21 | Ok(unsafe { |
fc512014 | 22 | // SAFETY: The raw pointer points to a valid value of type `T`. |
a2a8927a | 23 | let value = raw.read(); |
fc512014 XL |
24 | // SAFETY: Converts `Box<T>` to `Box<MaybeUninit<T>>` which is the |
25 | // inverse of `Box::assume_init()` and should be safe. | |
a2a8927a | 26 | let raw: Box<mem::MaybeUninit<T>> = Box::from_raw(raw.cast()); |
fc512014 | 27 | // SAFETY: Write the mapped value back into the `Box`. |
a2a8927a XL |
28 | Box::write(raw, f(value)?) |
29 | }) | |
fc512014 XL |
30 | } |
31 | } | |
32 | ||
33 | impl<T> IdFunctor for Vec<T> { | |
34 | type Inner = T; | |
35 | ||
36 | #[inline] | |
487cf647 | 37 | fn try_map_id<F, E>(self, f: F) -> Result<Self, E> |
fc512014 | 38 | where |
a2a8927a | 39 | F: FnMut(Self::Inner) -> Result<Self::Inner, E>, |
fc512014 | 40 | { |
487cf647 | 41 | self.into_iter().map(f).collect() |
fc512014 XL |
42 | } |
43 | } | |
44 | ||
45 | impl<T> IdFunctor for Box<[T]> { | |
46 | type Inner = T; | |
47 | ||
48 | #[inline] | |
a2a8927a | 49 | fn try_map_id<F, E>(self, f: F) -> Result<Self, E> |
fc512014 | 50 | where |
a2a8927a | 51 | F: FnMut(Self::Inner) -> Result<Self::Inner, E>, |
fc512014 | 52 | { |
a2a8927a | 53 | Vec::from(self).try_map_id(f).map(Into::into) |
fc512014 XL |
54 | } |
55 | } | |
56 | ||
57 | impl<I: Idx, T> IdFunctor for IndexVec<I, T> { | |
58 | type Inner = T; | |
59 | ||
60 | #[inline] | |
a2a8927a | 61 | fn try_map_id<F, E>(self, f: F) -> Result<Self, E> |
fc512014 | 62 | where |
a2a8927a | 63 | F: FnMut(Self::Inner) -> Result<Self::Inner, E>, |
fc512014 | 64 | { |
a2a8927a | 65 | self.raw.try_map_id(f).map(IndexVec::from_raw) |
fc512014 XL |
66 | } |
67 | } | |
9ffffee4 FG |
68 | |
69 | macro_rules! rc { | |
70 | ($($rc:ident),+) => {$( | |
71 | impl<T: Clone> IdFunctor for $rc<T> { | |
72 | type Inner = T; | |
73 | ||
74 | #[inline] | |
75 | fn try_map_id<F, E>(mut self, mut f: F) -> Result<Self, E> | |
76 | where | |
77 | F: FnMut(Self::Inner) -> Result<Self::Inner, E>, | |
78 | { | |
79 | // We merely want to replace the contained `T`, if at all possible, | |
80 | // so that we don't needlessly allocate a new `$rc` or indeed clone | |
81 | // the contained type. | |
82 | unsafe { | |
83 | // First step is to ensure that we have a unique reference to | |
84 | // the contained type, which `$rc::make_mut` will accomplish (by | |
85 | // allocating a new `$rc` and cloning the `T` only if required). | |
86 | // This is done *before* casting to `$rc<ManuallyDrop<T>>` so that | |
87 | // panicking during `make_mut` does not leak the `T`. | |
88 | $rc::make_mut(&mut self); | |
89 | ||
90 | // Casting to `$rc<ManuallyDrop<T>>` is safe because `ManuallyDrop` | |
91 | // is `repr(transparent)`. | |
92 | let ptr = $rc::into_raw(self).cast::<mem::ManuallyDrop<T>>(); | |
93 | let mut unique = $rc::from_raw(ptr); | |
94 | ||
95 | // Call to `$rc::make_mut` above guarantees that `unique` is the | |
96 | // sole reference to the contained value, so we can avoid doing | |
97 | // a checked `get_mut` here. | |
98 | let slot = $rc::get_mut_unchecked(&mut unique); | |
99 | ||
100 | // Semantically move the contained type out from `unique`, fold | |
101 | // it, then move the folded value back into `unique`. Should | |
102 | // folding fail, `ManuallyDrop` ensures that the "moved-out" | |
103 | // value is not re-dropped. | |
104 | let owned = mem::ManuallyDrop::take(slot); | |
105 | let folded = f(owned)?; | |
106 | *slot = mem::ManuallyDrop::new(folded); | |
107 | ||
108 | // Cast back to `$rc<T>`. | |
109 | Ok($rc::from_raw($rc::into_raw(unique).cast())) | |
110 | } | |
111 | } | |
112 | } | |
113 | )+}; | |
114 | } | |
115 | ||
116 | rc! { Rc, Arc } |