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