]>
Commit | Line | Data |
---|---|---|
a1dfa0c6 XL |
1 | //! `Cell` variant for (scoped) existential lifetimes. |
2 | ||
3 | use std::cell::Cell; | |
4 | use std::mem; | |
5 | use std::ops::{Deref, DerefMut}; | |
6 | ||
7 | /// Type lambda application, with a lifetime. | |
416331ca | 8 | #[allow(unused_lifetimes)] |
a1dfa0c6 XL |
9 | pub trait ApplyL<'a> { |
10 | type Out; | |
11 | } | |
12 | ||
0731742a | 13 | /// Type lambda taking a lifetime, i.e., `Lifetime -> Type`. |
a1dfa0c6 XL |
14 | pub trait LambdaL: for<'a> ApplyL<'a> {} |
15 | ||
16 | impl<T: for<'a> ApplyL<'a>> LambdaL for T {} | |
17 | ||
18 | // HACK(eddyb) work around projection limitations with a newtype | |
19 | // FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out` | |
20 | pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out); | |
21 | ||
22 | impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> { | |
23 | type Target = <T as ApplyL<'b>>::Out; | |
24 | fn deref(&self) -> &Self::Target { | |
25 | self.0 | |
26 | } | |
27 | } | |
28 | ||
29 | impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> { | |
30 | fn deref_mut(&mut self) -> &mut Self::Target { | |
31 | self.0 | |
32 | } | |
33 | } | |
34 | ||
35 | pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>); | |
36 | ||
37 | impl<T: LambdaL> ScopedCell<T> { | |
38 | pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self { | |
39 | ScopedCell(Cell::new(value)) | |
40 | } | |
41 | ||
9fa01778 | 42 | /// Sets the value in `self` to `replacement` while |
a1dfa0c6 XL |
43 | /// running `f`, which gets the old value, mutably. |
44 | /// The old value will be restored after `f` exits, even | |
45 | /// by panic, including modifications made to it by `f`. | |
46 | pub fn replace<'a, R>( | |
47 | &self, | |
48 | replacement: <T as ApplyL<'a>>::Out, | |
49 | f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R, | |
50 | ) -> R { | |
51 | /// Wrapper that ensures that the cell always gets filled | |
52 | /// (with the original state, optionally changed by `f`), | |
53 | /// even if `f` had panicked. | |
54 | struct PutBackOnDrop<'a, T: LambdaL> { | |
55 | cell: &'a ScopedCell<T>, | |
56 | value: Option<<T as ApplyL<'static>>::Out>, | |
57 | } | |
58 | ||
59 | impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> { | |
60 | fn drop(&mut self) { | |
61 | self.cell.0.set(self.value.take().unwrap()); | |
62 | } | |
63 | } | |
64 | ||
65 | let mut put_back_on_drop = PutBackOnDrop { | |
66 | cell: self, | |
67 | value: Some(self.0.replace(unsafe { | |
68 | let erased = mem::transmute_copy(&replacement); | |
69 | mem::forget(replacement); | |
70 | erased | |
71 | })), | |
72 | }; | |
73 | ||
74 | f(RefMutL(put_back_on_drop.value.as_mut().unwrap())) | |
75 | } | |
76 | ||
9fa01778 | 77 | /// Sets the value in `self` to `value` while running `f`. |
416331ca | 78 | pub fn set<R>(&self, value: <T as ApplyL<'_>>::Out, f: impl FnOnce() -> R) -> R { |
a1dfa0c6 XL |
79 | self.replace(value, |_| f()) |
80 | } | |
81 | } |