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
//! This module deals with interior mutability, which [undermines][1] the
//! soundness of runtime uses of `const`-allocated heap objects.
//!
//! [1]: https://github.com/rust-lang/rust/pull/91884#discussion_r774659436
use core::fmt;

// FIXME: Get rid of `Frozen` if one of the folllowing things happens:
//        (1) It's decided that interior mutability implies `!Copy`.
//        (2) A trait indicating the absence of interior mutability, such as
//        the one proposed by the now-closed [RFC 2944], is added to `core`.
//        [ref:missing_interior_mutability_trait]
//
// [RFC 2944]: https://github.com/rust-lang/rfcs/pull/2944
//
// <https://github.com/rust-lang/rust/issues/25053#issuecomment-493742957>:
//
// > Basically, currently we have no interior-mutable-`Copy` type, but that is
// > an accident. And we also have legit needs for `Copy` interior mutable
// > types, which is irreconcilable with using `Copy` to exclude interior
// > mutability.

/// Erases interior mutability by preventing reference forming.
#[repr(transparent)]
pub struct Frozen<T: ?Sized>(T);

impl<T: Copy> Frozen<T> {
    /// Get a copy of the contained `T`.
    #[inline]
    pub const fn get(&self) -> T {
        self.0
    }

    #[macropol::macropol]
    /// Copy the referenced `[T]` to the CTFE heap. The resulting reference can
    /// be safely consumed at runtime.
    ///
    /// # Example
    ///
    /// ```rust
    /// use ${env!("CARGO_CRATE_NAME")}::utils::Frozen;
    /// const SLICE: &[Frozen<u8>] = Frozen::leak_slice(&[1, 2, 3]);
    /// assert_eq!(SLICE[1].get(), 2);
    /// ```
    pub const fn leak_slice<'out>(x: &[T]) -> &'out [Frozen<T>]
    where
        T: 'out,
    {
        let size = core::mem::size_of::<T>()
            .checked_mul(x.len())
            .expect("size overflow");
        let align = core::mem::align_of::<T>();

        if size == 0 {
            return &[];
        }

        unsafe {
            // Allocate a CTFE heap memory block
            let ptr = core::intrinsics::const_allocate(size, align).cast::<T>();
            assert!(
                !ptr.guaranteed_eq(core::ptr::null_mut()).unwrap_or(false),
                "heap allocation failed"
            );

            // Copy the `[T]` onto it
            core::ptr::copy_nonoverlapping(x.as_ptr(), ptr, x.len());

            // Reinterpret it as `[Frozen<T>]` (it's safe because of
            // `repr(transparent)`)
            let ptr = ptr.cast::<Frozen<T>>();

            // Turn `ptr` into a alice reference
            core::slice::from_raw_parts(ptr, x.len())
        }
    }
}

impl<T: Copy> Copy for Frozen<T> {}

impl<T: Copy> const Clone for Frozen<T> {
    #[inline]
    fn clone(&self) -> Self {
        // Don't use `T as Clone` because it could expose interior mutability.
        *self
    }
}

impl<T: Copy + fmt::Debug> fmt::Debug for Frozen<T> {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        self.get().fmt(f)
    }
}