]> git.lizzy.rs Git - rust.git/blob - src/libproc_macro/bridge/scoped_cell.rs
Various minor/cosmetic improvements to code
[rust.git] / src / libproc_macro / bridge / scoped_cell.rs
1 // Copyright 2018 The Rust Project Developers. See the COPYRIGHT
2 // file at the top-level directory of this distribution and at
3 // http://rust-lang.org/COPYRIGHT.
4 //
5 // Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
6 // http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
7 // <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
8 // option. This file may not be copied, modified, or distributed
9 // except according to those terms.
10
11 //! `Cell` variant for (scoped) existential lifetimes.
12
13 use std::cell::Cell;
14 use std::mem;
15 use std::ops::{Deref, DerefMut};
16
17 /// Type lambda application, with a lifetime.
18 pub trait ApplyL<'a> {
19     type Out;
20 }
21
22 /// Type lambda taking a lifetime, i.e., `Lifetime -> Type`.
23 pub trait LambdaL: for<'a> ApplyL<'a> {}
24
25 impl<T: for<'a> ApplyL<'a>> LambdaL for T {}
26
27 // HACK(eddyb) work around projection limitations with a newtype
28 // FIXME(#52812) replace with `&'a mut <T as ApplyL<'b>>::Out`
29 pub struct RefMutL<'a, 'b, T: LambdaL>(&'a mut <T as ApplyL<'b>>::Out);
30
31 impl<'a, 'b, T: LambdaL> Deref for RefMutL<'a, 'b, T> {
32     type Target = <T as ApplyL<'b>>::Out;
33     fn deref(&self) -> &Self::Target {
34         self.0
35     }
36 }
37
38 impl<'a, 'b, T: LambdaL> DerefMut for RefMutL<'a, 'b, T> {
39     fn deref_mut(&mut self) -> &mut Self::Target {
40         self.0
41     }
42 }
43
44 pub struct ScopedCell<T: LambdaL>(Cell<<T as ApplyL<'static>>::Out>);
45
46 impl<T: LambdaL> ScopedCell<T> {
47     pub const fn new(value: <T as ApplyL<'static>>::Out) -> Self {
48         ScopedCell(Cell::new(value))
49     }
50
51     /// Set the value in `self` to `replacement` while
52     /// running `f`, which gets the old value, mutably.
53     /// The old value will be restored after `f` exits, even
54     /// by panic, including modifications made to it by `f`.
55     pub fn replace<'a, R>(
56         &self,
57         replacement: <T as ApplyL<'a>>::Out,
58         f: impl for<'b, 'c> FnOnce(RefMutL<'b, 'c, T>) -> R,
59     ) -> R {
60         /// Wrapper that ensures that the cell always gets filled
61         /// (with the original state, optionally changed by `f`),
62         /// even if `f` had panicked.
63         struct PutBackOnDrop<'a, T: LambdaL> {
64             cell: &'a ScopedCell<T>,
65             value: Option<<T as ApplyL<'static>>::Out>,
66         }
67
68         impl<'a, T: LambdaL> Drop for PutBackOnDrop<'a, T> {
69             fn drop(&mut self) {
70                 self.cell.0.set(self.value.take().unwrap());
71             }
72         }
73
74         let mut put_back_on_drop = PutBackOnDrop {
75             cell: self,
76             value: Some(self.0.replace(unsafe {
77                 let erased = mem::transmute_copy(&replacement);
78                 mem::forget(replacement);
79                 erased
80             })),
81         };
82
83         f(RefMutL(put_back_on_drop.value.as_mut().unwrap()))
84     }
85
86     /// Set the value in `self` to `value` while running `f`.
87     pub fn set<'a, R>(&self, value: <T as ApplyL<'a>>::Out, f: impl FnOnce() -> R) -> R {
88         self.replace(value, |_| f())
89     }
90 }