]> git.lizzy.rs Git - rust.git/blob - library/proc_macro/src/bridge/scoped_cell.rs
Rollup merge of #90782 - ricobbe:binutils-dlltool, r=michaelwoerister
[rust.git] / library / proc_macro / src / 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 #[allow(unused_lifetimes)]
9 pub trait ApplyL<'a> {
10     type Out;
11 }
12
13 /// Type lambda taking a lifetime, i.e., `Lifetime -> Type`.
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     #[rustc_allow_const_fn_unstable(const_fn)]
39     pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self {
40         ScopedCell(Cell::new(value))
41     }
42
43     /// Sets the value in `self` to `replacement` while
44     /// running `f`, which gets the old value, mutably.
45     /// The old value will be restored after `f` exits, even
46     /// by panic, including modifications made to it by `f`.
47     pub fn replace<'a, R>(
48         &self,
49         replacement: <T as ApplyL<'a>>::Out,
50         f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R,
51     ) -> R {
52         /// Wrapper that ensures that the cell always gets filled
53         /// (with the original state, optionally changed by `f`),
54         /// even if `f` had panicked.
55         struct PutBackOnDrop<'a, T: LambdaL> {
56             cell: &'a ScopedCell<T>,
57             value: Option<<T as ApplyL<'static>>::Out>,
58         }
59
60         impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> {
61             fn drop(&mut self) {
62                 self.cell.0.set(self.value.take().unwrap());
63             }
64         }
65
66         let mut put_back_on_drop = PutBackOnDrop {
67             cell: self,
68             value: Some(self.0.replace(unsafe {
69                 let erased = mem::transmute_copy(&replacement);
70                 mem::forget(replacement);
71                 erased
72             })),
73         };
74
75         f(RefMutL(put_back_on_drop.value.as_mut().unwrap()))
76     }
77
78     /// Sets the value in `self` to `value` while running `f`.
79     pub fn set<R>(&self, value: <T as ApplyL<'_>>::Out, f: impl FnOnce() -> R) -> R {
80         self.replace(value, |_| f())
81     }
82 }