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
#![doc = include_str!("../README.md")]

use core::mem;
use std::{process, hint::unreachable_unchecked};

/// Returns reference with lifetime of value
///
/// # Safety
/// * You must not move original variable
/// * You must not create immutable reference while having mutable reference
/// * The reference must not outlive
#[inline(always)]
pub unsafe fn extend_ref<'a, T: 'a>(target: &T) -> &'a T {
    mem::transmute::<&T, &'a T>(target)
}

/// Returns mutable reference with lifetime of value
///
/// # Safety
/// * You must not move original variable
/// * You must not create mutable reference while having immutable references
/// * You must not create multiple mutable reference
/// * The reference must not outlive
#[inline(always)]
pub unsafe fn extend_mut<'a, T: 'a>(target: &mut T) -> &'a mut T {
    mem::transmute::<&mut T, &'a mut T>(target)
}

#[doc(hidden)]
#[inline(always)]
pub fn run_abort_guarded<F: FnOnce()>(func: F) -> ! {
    struct AbortGuard;

    impl Drop for AbortGuard {
        fn drop(&mut self) {
            process::abort();
        }
    }

    {
        let _guard = AbortGuard;

        func();
    }

    // Safety: AbortGuard always abort process on drop and cannot be touched from outside
    unsafe { unreachable_unchecked() }
}

/// Safely extends lifetime of reference as long as lifetime of it's value.
///
/// ## Usage
/// ```Rust
/// ref_extended(|&a| {}); // a is &'static u32 because lifetime of a's value is 'static
///
/// ref_extended(|&a, &mut b| {}); // a is &'static u32 and b is &'static mut u32 because lifetime of a and b' value is 'static
/// ```
#[macro_export]
macro_rules! ref_extended {
    (|$(& $($var: ident)+ ),*| $body: expr) => {
        $(let ::ref_extended::extract_ref_name!($($var)*) = unsafe { ::ref_extended::extend_lifetime!($($var)*) };)*

        ::ref_extended::run_abort_guarded(move || {$body})
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! extract_ref_name {
    ($name: ident) => {
        $name
    };

    (mut $name: ident) => {
        $name
    };
}

#[doc(hidden)]
#[macro_export]
macro_rules! extend_lifetime {
    ($name: ident) => {
        ::ref_extended::extend_ref(&$name)
    };

    (mut $name: ident) => {
        ::ref_extended::extend_mut(&mut $name)
    };
}