static_init 1.0.3

Safe mutable static and non const static initialization, and code execution at program startup/exit.
Documentation
pub use static_impl::{ConstStatic, Static, __set_init_prio};

#[cfg(debug_mode)]
mod static_impl {

    use crate::{FinalyMode, InitMode, StaticInfo};

    use core::cmp::Ordering::*;
    use core::mem::MaybeUninit;
    use core::ops::{Deref, DerefMut};
    use core::sync::atomic::{AtomicI32, Ordering};

    /// The actual type of mutable *dynamic statics*.
    ///
    /// It implements `Deref<Target=T>` and `DerefMut`.
    ///
    /// All associated functions are only usefull for the implementation of
    /// the `dynamic` proc macro attribute
    pub struct Static<T>(MaybeUninit<T>, StaticInfo, AtomicI32);

    /// The actual type of non mutable *dynamic statics*.
    ///
    /// It implements `Deref<Target=T>`.
    ///
    /// All associated functions are only usefull for the implementation of
    /// the `dynamic` proc macro attribute
    pub struct ConstStatic<T>(Static<T>);

    static CUR_INIT_PRIO: AtomicI32 = AtomicI32::new(i32::MIN);

    static CUR_DROP_PRIO: AtomicI32 = AtomicI32::new(i32::MIN);

    #[doc(hidden)]
    #[inline]
    pub fn __set_init_prio(v: i32) {
        CUR_INIT_PRIO.store(v, Ordering::Relaxed);
    }

    impl<T> Static<T> {
        #[inline]
        /// Build an uninitialized Static
        ///
        /// # Safety
        ///
        /// The target object should be a mutable statics to ensure
        /// that all accesses to this object are unsafe.
        pub const unsafe fn uninit(info: StaticInfo) -> Self {
            Self(MaybeUninit::uninit(), info, AtomicI32::new(0))
        }
        #[inline]
        pub const fn from(v: T, info: StaticInfo) -> Self {
            Static(MaybeUninit::new(v), info, AtomicI32::new(1))
        }

        #[inline]
        pub fn set_to(this: &'static mut Self, v: T) {
            this.0 = MaybeUninit::new(v);
            this.2.store(1, Ordering::Relaxed);
        }

        #[inline]
        /// # Safety
        ///
        /// The objet should not be accessed after this call
        pub unsafe fn drop(this: &'static mut Self) {
            if let FinalyMode::ProgramDestructor(prio) = &this.1.drop_mode {
                CUR_DROP_PRIO.store(*prio as i32, Ordering::Relaxed);
                this.0.as_mut_ptr().drop_in_place();
                CUR_DROP_PRIO.store(i32::MIN, Ordering::Relaxed);
            } else {
                this.0.as_mut_ptr().drop_in_place();
            };
            this.2.store(2, Ordering::Relaxed);
        }
    }

    #[inline]
    fn check_access(info: &StaticInfo, status: i32) {
        if status == 0 {
            core::panic!(
                "Attempt to access variable {:#?} before it is initialized during initialization \
                 priority {}. Tip: increase init priority of this static to a value larger than \
                 {prio} (attribute syntax: `#[dynamic(init=<prio>)]`)",
                info,
                prio = CUR_INIT_PRIO.load(Ordering::Relaxed)
            )
        }
        if status == 2 {
            core::panic!(
                "Attempt to access variable {:#?} after it was destroyed during destruction \
                 priority {prio}. Tip increase drop priority of this static to a value larger \
                 than {prio} (attribute syntax: `#[dynamic(drop=<prio>)]`)",
                info,
                prio = CUR_DROP_PRIO.load(Ordering::Relaxed)
            )
        }
        let init_prio = CUR_INIT_PRIO.load(Ordering::Relaxed);
        let drop_prio = CUR_DROP_PRIO.load(Ordering::Relaxed);

        if let FinalyMode::ProgramDestructor(prio) = &info.drop_mode {
            match drop_prio.cmp(&(*prio as i32)) {
                Equal => core::panic!(
                    "This access to variable {:#?} is not sequenced before to its drop. Tip \
                     increase drop priority of this static to a value larger than {prio} \
                     (attribute syntax: `#[dynamic(drop=<prio>)]`)",
                    info,
                    prio = drop_prio
                ),
                Greater => core::panic!(
                    "Unexpected initialization order while accessing {:#?} from drop priority {}. \
                     This is a bug of `static_init` library, please report \"
             the issue inside `static_init` repository.",
                    info,
                    drop_prio
                ),
                Less => (),
            }
        }

        if let InitMode::ProgramConstructor(prio) = &info.init_mode {
            match init_prio.cmp(&(*prio as i32)) {
                Equal => core::panic!(
                    "This access to variable {:#?} is not sequenced after construction of this \
                     static. Tip increase init priority of this static to a value larger than \
                     {prio} (attribute syntax: `#[dynamic(init=<prio>)]`)",
                    info,
                    prio = init_prio
                ),
                Greater => core::panic!(
                    "Unexpected initialization order while accessing {:#?} from init priority {}. \
                     This is a bug of `static_init` library, please report \"
             the issue inside `static_init` repository.",
                    info,
                    init_prio,
                ),
                Less => (),
            }
        }
    }

    impl<T> Deref for Static<T> {
        type Target = T;
        #[inline(always)]
        fn deref(&self) -> &T {
            check_access(&self.1, self.2.load(Ordering::Relaxed));
            // SAFETY: The object is either
            //  - built with `uninit`, in which case it is a mutable static
            //    so all access path to it are unsafe
            //  - or built with `from` in which case it is necessarily initialized
            unsafe { &*self.0.as_ptr() }
        }
    }
    impl<T> DerefMut for Static<T> {
        #[inline(always)]
        fn deref_mut(&mut self) -> &mut T {
            check_access(&self.1, self.2.load(Ordering::Relaxed));
            // SAFETY: The object is either
            //  - built with `uninit`, in which case it is a mutable static
            //    so all access path to it are unsafe
            //  - or built with `from` in which case it is necessarily initialized
            unsafe { &mut *self.0.as_mut_ptr() }
        }
    }

    impl<T> ConstStatic<T> {
        #[inline]
        /// Build an uninitialized ConstStatic
        ///
        /// # Safety
        ///
        /// The target object should be a mutable static to
        /// ensure that all accesses to the object are unsafe.
        pub const unsafe fn uninit(info: StaticInfo) -> Self {
            Self(Static::uninit(info))
        }
        #[inline]
        pub const fn from(v: T, info: StaticInfo) -> Self {
            Self(Static::from(v, info))
        }
        #[inline]
        /// # Safety
        ///
        /// The reference to self should be unique.
        pub fn set_to(this: &'static mut Self, v: T) {
            Static::set_to(&mut this.0, v)
        }
        #[inline]
        /// # Safety
        ///
        /// The objet should not be accessed after this call
        pub unsafe fn drop(this: &'static mut Self) {
            Static::drop(&mut this.0);
        }
    }

    impl<T> Deref for ConstStatic<T> {
        type Target = T;
        #[inline(always)]
        fn deref(&self) -> &T {
            // SAFETY: The object is either
            //  - built with `uninit`, in which case it is a mutable static
            //    so all access path to it are unsafe
            //  - or built with `from` in which case it is necessarily initialized
            &*self.0
        }
    }
}

#[cfg(not(debug_mode))]
mod static_impl {
    use core::mem::MaybeUninit;
    use core::ops::{Deref, DerefMut};

    /// The actual type of mutable *dynamic statics*.
    ///
    /// It implements `Deref<Target=T>` and `DerefMut`.
    ///
    /// All associated functions are only usefull for the implementation of
    /// the `dynamic` proc macro attribute
    pub struct Static<T>(MaybeUninit<T>);

    /// The actual type of non mutable *dynamic statics*.
    ///
    /// It implements `Deref<Target=T>`.
    ///
    /// All associated functions are only usefull for the implementation of
    /// the `dynamic` proc macro attribute
    pub struct ConstStatic<T>(Static<T>);

    #[doc(hidden)]
    #[inline(always)]
    pub fn __set_init_prio(_: i32) {}

    //As a trait in order to avoid noise;
    impl<T> Static<T> {
        #[inline]
        /// Build a new static.
        ///
        /// # Safety
        ///
        /// The target object must be a mutable static
        pub const unsafe fn uninit() -> Self {
            Self(MaybeUninit::uninit())
        }
        #[inline]
        pub const fn from(v: T) -> Self {
            Self(MaybeUninit::new(v))
        }

        #[inline]
        pub fn set_to(this: &'static mut Self, v: T) {
            this.0 = MaybeUninit::new(v);
        }

        #[inline]
        /// Drop the inner object
        ///
        /// # Safety
        ///
        /// The object should have been previously initialized
        pub unsafe fn drop(this: &'static mut Self) {
            this.0.as_mut_ptr().drop_in_place();
        }
    }

    impl<T> Deref for Static<T> {
        type Target = T;
        #[inline(always)]
        fn deref(&self) -> &T {
            // SAFETY: The object is either
            //  - built with `uninit`, in which case it is a mutable static
            //    so all access path to it are unsafe
            //  - or built with `from` in which case it is necessarily initialized
            unsafe { &*self.0.as_ptr() }
        }
    }
    impl<T> DerefMut for Static<T> {
        #[inline(always)]
        fn deref_mut(&mut self) -> &mut T {
            // SAFETY: The object is either
            //  - built with `uninit`, in which case it is a mutable static
            //    so all access path to it are unsafe
            //  - or built with `from` in which case it is necessarily initialized
            unsafe { &mut *self.0.as_mut_ptr() }
        }
    }

    impl<T> ConstStatic<T> {
        #[inline]
        /// Build a new ConstStatic
        ///
        /// # Safety
        ///
        /// The target object must be a mutable static
        pub const unsafe fn uninit() -> Self {
            Self(Static::uninit())
        }
        #[inline]
        pub const fn from(v: T) -> Self {
            Self(Static::from(v))
        }
        #[inline]
        pub fn set_to(this: &'static mut Self, v: T) {
            Static::set_to(&mut this.0, v)
        }
        #[inline]
        /// # Safety
        ///
        /// The object should have been previously initialized
        pub unsafe fn drop(this: &'static mut Self) {
            Static::drop(&mut this.0);
        }
    }

    impl<T> Deref for ConstStatic<T> {
        type Target = T;
        #[inline(always)]
        fn deref(&self) -> &T {
            // SAFETY: The object is either
            //  - built with `uninit`, in which case it is a mutable static
            //    so all access path to it are unsafe
            //  - or built with `from` in which case it is necessarily initialized
            &*self.0
        }
    }
}