1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
/* staticwrap.rs
 *
 * Developed by Tim Walls <tim.walls@snowgoons.com>
 * Copyright (c) All Rights Reserved, Tim Walls
 */
//! Turn locals that we will never drop into static references.


// Imports ===================================================================
use core::cell::{Ref, RefCell, RefMut};
use core::ops::{Deref, DerefMut};
use avr_oxide::alloc::boxed::Box;

// Declarations ==============================================================

/// In an embedded environment, we often need static references to things,
/// for example because we are passing them on to an interrupt service
/// routine.
///
/// We also often need to do some non-const setup on those things, something
/// Rust makes quite ugly (mutable statics are unsafe and unpleasant to work
/// with.)
///
/// Fortunately, we know that if our `main` routine never returns (i.e. it
/// is declared `-> !`) we will never drop any locals there, and can thus
/// safely get static references to them right?  Well, right in that *we*
/// know that - but for 'reasons' (`panic_unwind`, I'm glaring at you),
/// the Rust compiler doesn't know that.
///
/// Thus, `StaticWrap<T>`.  This will wrap a variable that *you* know
/// will never be dropped, and then give you access to static references
/// via the [`StaticWrap::static_ref()`] and [`StaticWrap::static_ref_mut()`]
/// methods.
///
/// The contract you promise to obey is that you will indeed never drop
/// them of course.  This is enforced at runtime - sropping a `StaticWrap`
/// is a *fatal runtime error*.
///
/// The normal borrowing rules - i.e. a maximum of one mutable borrow,
/// and no mutable borrows if there are immutable borrows - for a variable
/// placed inside a StaticWrap, are enforced at runtime.
pub struct StaticWrap<T: 'static> {
  wrapped: *mut RefCell<T>
}

pub struct StaticRef<'sr, T: ?Sized + 'sr> {
  r: Ref<'sr,T>
}

pub struct StaticRefMut<'sr, T: ?Sized + 'sr>  {
  rm: RefMut<'sr,T>
}

pub trait AsStaticRef<T>
where
  T: ?Sized
{
  unsafe fn as_static_ref(&self) -> &'static T;
}

pub trait AsStaticRefMut<T> : AsStaticRef<T>
  where
    T: ?Sized
{
  unsafe fn as_static_ref_mut(&mut self) -> &'static mut T;
}


// Code ======================================================================
impl<'sr,T: ?Sized + 'sr> AsStaticRef<T> for StaticRef<'sr, T> {
  unsafe fn as_static_ref(&self) -> &'static T {
    self.static_ref()
  }
}

impl<'sr,T: ?Sized + 'sr> AsStaticRef<T> for StaticRefMut<'sr, T> {
  unsafe fn as_static_ref(&self) -> &'static T {
    self.static_ref()
  }
}

impl<'sr,T: ?Sized + 'sr> AsStaticRefMut<T> for StaticRefMut<'sr, T> {
  unsafe fn as_static_ref_mut(&mut self) -> &'static mut T {
    self.static_ref_mut()
  }
}

impl<T: ?Sized> AsStaticRef<T> for &'static T {
  unsafe fn as_static_ref(&self) -> &'static T {
    self
  }
}




impl<T> StaticWrap<T> {
  /// Create a new StaticWrap around the given variable.  You may then
  /// access static references using
  /// [`StaticWrap::static_ref()`] and [`StaticWrap::static_ref_mut()`].
  ///
  /// # Warning
  /// You promise this instance *will never be dropped*.  If it is, a fatal
  /// runtime error will result.
  pub fn new(wrapping: T) -> Self {
    StaticWrap {
      wrapped: Box::leak(Box::new(RefCell::new(wrapping)))
    }
  }

  /// Obtain a static reference to the contained instance
  pub fn borrow(&self) -> StaticRef<'static,T> {
    unsafe {
      StaticRef {
        r: match (&*self.wrapped).try_borrow() {
          Ok(b) => b,
          #[cfg(not(feature="panic_unwind"))]
          Err(_e) => {
            avr_oxide::oserror::halt(avr_oxide::oserror::OsError::StaticBorrow);
          }
          #[cfg(feature="panic_unwind")]
          Err(e) => {
            panic!(e);
          }
        }
      }
    }
  }

  /// Obtain a mutable static reference to the contained instance.
  ///
  /// # SAFETY
  /// We don't (or rather, the borrow checker can't) enforce the limitation
  /// that a mutual reference should be exclusive.  Be careful what you wish
  /// for.
  pub fn borrow_mut(&mut self) -> StaticRefMut<'static, T> {
    unsafe {
      StaticRefMut {
        rm: match (&mut *self.wrapped).try_borrow_mut() {
          Ok(b) => b,
          #[cfg(not(feature="panic_unwind"))]
          Err(_e) => {
            avr_oxide::oserror::halt(avr_oxide::oserror::OsError::StaticBorrow);
          }
          #[cfg(feature="panic_unwind")]
          Err(e) => {
            panic!(e);
          }
        }
      }
    }
  }
}

impl<'sr,T: ?Sized + 'sr> Deref for StaticRef<'sr, T> {
  type Target = T;

  fn deref(&self) -> &Self::Target {
    &*self.r
  }
}

impl<'sr,T: ?Sized + 'sr> Deref for StaticRefMut<'sr, T> {
  type Target = T;

  fn deref(&self) -> &Self::Target {
    &*self.rm
  }
}

impl<'sr,T: ?Sized + 'sr> DerefMut for StaticRefMut<'sr, T> {
  fn deref_mut(&mut self) -> &mut Self::Target {
    &mut *self.rm
  }
}

impl<'sr,T: ?Sized + 'sr> StaticRef<'sr, T> {
  /// Return a reference to the contained object with a `'static` lifetime.
  ///
  /// # Safety
  /// The returned reference is guaranteed to be valid for the `'static`
  /// lifetime.  However, this remains unsafe because it allows the caller
  /// to evade the normal borrow checker rules about not holding more than
  /// one mutable reference to the contents of the type.
  pub unsafe fn static_ref(&self) -> &'static T {
    core::mem::transmute(self.deref())
  }

  pub fn clone(orig: &Self) -> Self {
    StaticRef {
      r: Ref::clone(&orig.r)
    }
  }
}

impl<'sr,T: ?Sized + 'sr> StaticRefMut<'sr, T> {
  /// Return a reference to the contained object with a `'static` lifetime.
  ///
  /// # Safety
  /// The returned reference is guaranteed to be valid for the `'static`
  /// lifetime.  However, this remains unsafe because it allows the caller
  /// to evade the normal borrow checker rules about not holding more than
  /// one mutable reference to the contents of the type.
  pub unsafe fn static_ref(&self) -> &'static T {
    core::mem::transmute(self.deref())
  }

  /// Return a mutable reference to the contained object with a `'static` lifetime.
  ///
  /// # Safety
  /// The returned reference is guaranteed to be valid for the `'static`
  /// lifetime.  However, this remains unsafe because it allows the caller
  /// to evade the normal borrow checker rules about not holding more than
  /// one mutable reference to the contents of the type.
  pub unsafe fn static_ref_mut(&mut self) -> &'static mut T {
    core::mem::transmute(self.deref_mut())
  }
}

#[cfg(feature="runtime_checks")]
impl<T> Drop for StaticWrap<T> {
  fn drop(&mut self) {
    #[cfg(not(test))]
    avr_oxide::oserror::halt(avr_oxide::oserror::OsError::StaticDropped);
    #[cfg(test)]
    println!("((StaticWrap dropped - only allowed in testing))");
  }
}

unsafe impl<'sr,T: ?Sized + Send + 'sr> Send for StaticRef<'sr,T> {}
unsafe impl<'sr,T: ?Sized + Sync + 'sr> Sync for StaticRef<'sr,T> {}

unsafe impl<'sr,T: ?Sized + Send + 'sr> Send for StaticRefMut<'sr,T> {}
unsafe impl<'sr,T: ?Sized + Sync + 'sr> Sync for StaticRefMut<'sr,T> {}


// Tests =====================================================================
#[cfg(test)]
mod tests {
  use avr_oxide::StaticWrap;

  #[test]
  fn test_multiple_borrows() {
    let thing = StaticWrap::new(0x123);

    let borrow_one = thing.borrow();
    let borrow_two = thing.borrow();
  }

  #[test]
  #[should_panic]
  fn test_no_mut_with_immut() {
    let mut thing = StaticWrap::new(0x123);

    let borrow_one = thing.borrow();
    let borrow_two = thing.borrow_mut();
  }
}