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
//! Allows defining traits whose functions/methods can be safe/unsafe
//! to call depending on the implementor.
//!
//! # Example
//! An alternative implementation of Default whose safety is determined by the implementor.
//!
//! ```
//! # #[macro_use]
//! # extern crate core_extensions;
//! use core_extensions::maybe_unsafe::{IsSafe,IsUnsafe,MaybeUnsafe};
//! use std::{fmt,mem};
//!
//! # fn main(){
//!
//! /// Alternative definition of Default.
//! trait AlternativeDefault{
//!     /// Whether AlternativeDefault::default is safe to call.
//!     ///
//!     /// If Safety=IsUnsafe one must consult the documentation of the implementor
//!     /// to check what must be done to maintain safety.
//!     type Safety:MaybeUnsafe;
//!
//!     fn default(safety:&Self::Safety)->Self;
//! }
//!
//! #[derive(Debug,PartialEq)]
//! pub struct ZeroInit<T>(pub T);
//!
//! #[derive(Debug,PartialEq)]
//! pub struct WithDefault<T>(pub T);
//!
//! /// # Safety
//! ///
//! /// Make sure to use this only on types on which 0 is a valid bit pattern.
//! impl<T> AlternativeDefault for ZeroInit<T> {
//!     type Safety=IsUnsafe;
//!     fn default(safety:&IsUnsafe)->Self{
//!         unsafe_!{safety=>
//!             ZeroInit(mem::zeroed())
//!         }
//!     }
//! }
//!
//! impl<T> AlternativeDefault for WithDefault<T>
//! where T:Default
//! {
//!     type Safety=IsSafe;
//!     fn default(_:&IsSafe)->Self{
//!         WithDefault(Default::default())
//!     }
//! }
//!
//! fn no_unsafe<T,U,F>(v:&Option<T>,f:F)->U
//! where T:fmt::Debug+AlternativeDefault<Safety=IsSafe>,
//!       F:FnOnce(&T)->U,
//! {
//!     match v.as_ref() {
//!         Some(v)=>f(v),
//!         None=>f(&AlternativeDefault::default(&())),
//!     }
//! }
//!
//! fn uses_unsafe(v:Option<ZeroInit<usize>>)->usize{
//!     v.unwrap_or_else(||unsafe{
//!         //zeroing a usize is fine.
//!         IsUnsafe::with(AlternativeDefault::default)
//!     }).0
//! }
//!
//! no_unsafe(&None::<WithDefault<bool>> ,|v| assert_eq!(v.0,false) );
//! no_unsafe(&Some(WithDefault(true))   ,|v| assert_eq!(v.0,true) );
//! no_unsafe(&None::<WithDefault<usize>>,|v| assert_eq!(v.0,0)  );
//! no_unsafe(&Some(WithDefault(10))     ,|v| assert_eq!(v.0,10) );
//!
//! assert_eq!(uses_unsafe(Some(ZeroInit(10))),10);
//! assert_eq!(uses_unsafe(None              ),0);
//!
//!
//! # }
//!
//! ```
//!

use std_::borrow::Borrow;

/// The trait used to choose whether traits' function/method
/// is safe(using IsSafe) or unsafe(using IsUnsafe) to call.
///
/// When passing a MaybeUnsafe as a parameter it is recommended to use an
/// `impl AsRef<IsSafe>`/`impl AsRef<IsUnsafe>` parameter for convenience.
///
/// This trait has a Sealed super trait to prevent users of this library from implementing it.
///
/// For examples of how to use this [look at the module-level documentation](index.html).
pub trait MaybeUnsafe: Sealed {
    /// Constructs a MaybeUnsafe,and passes it by reference to prevent
    /// it from escaping this function call.
    ///
    /// This is unsafe because it applies to both IsSafe and IsUnsafe.
    unsafe fn with<F, U>(f: F) -> U
    where
        F: FnOnce(&Self) -> U;
}

/// Represents the `safe` effect.
///
/// Functions taking this as a parameter should not to be unsafe.
///
/// For examples of how to use this [look at the module-level documentation](index.html).
pub type IsSafe = ();

impl MaybeUnsafe for IsSafe {
    unsafe fn with<F, U>(f: F) -> U
    where
        F: FnOnce(&Self) -> U,
    {
        f(&())
    }
}

/// Represents the `unsafe` effect.
///
/// Functions taking this as a parameter are equivalent to `unsafe fn`.
///
/// For examples of how to use this [look at the module-level documentation](index.html).
#[derive(Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
pub struct IsUnsafe(());

impl MaybeUnsafe for IsUnsafe {
    unsafe fn with<F, U>(f: F) -> U
    where
        F: FnOnce(&Self) -> U,
    {
        f(&IsUnsafe(()))
    }
}

impl Borrow<IsSafe> for IsUnsafe {
    fn borrow(&self) -> &IsSafe {
        static UNIT: &() = &();
        UNIT
    }
}

/// Macro for correctly using unsafe{} blocks inside functions that take IsUnsafe references.
///
/// This macro ensures that an IsUnsafe reference was provided to use an unsafe block.
///
/// For more information about IsUnsafe/IsSafe/MaybeUnsafe look at the
/// [maybe_unsafe module](./maybe_unsafe/index.html)
///
/// # Example
/// ```
/// # #[macro_use]
/// # extern crate core_extensions;
/// use core_extensions::maybe_unsafe::IsUnsafe;
/// use std::mem;
///
/// # fn main(){
/// /// Returns a zero initialized Copy value.
/// ///
/// /// # Safety
/// ///
/// /// 0 must be a valid bitpattern for the returned value.
/// fn zeroed_copy<T>(safety:&IsUnsafe)->T
/// where
///     T:Copy,
/// {
///     unsafe_!{safety=>
///         mem::zeroed()
///     }
/// }
///
/// # }
/// ```
#[macro_export]
macro_rules! unsafe_ {
    ($is_unsafe:expr=> $($tt:tt)* ) => {{
        let _:&$crate::maybe_unsafe::IsUnsafe=
            $crate::std_::borrow::Borrow::borrow(&$is_unsafe);
        unsafe{
            $($tt)*
        }
    }}
}

/////////////////////////////////////////////////////////////////////
///////                         SEALED              /////////////////
/////////////////////////////////////////////////////////////////////

mod sealed {
    use super::{IsSafe, IsUnsafe};
    pub trait Sealed {}
    impl Sealed for IsSafe {}
    impl Sealed for IsUnsafe {}
}
use self::sealed::Sealed;