Skip to content

Commit 7ff1316

Browse files
committed
feat(utils): add Frozen
`Frozen` can be used to return dynamically-sized const evaluation results in a sound manner. It can be removed when we have a better way to deal with interior mutability. > 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`.
1 parent f2bd8e1 commit 7ff1316

File tree

2 files changed

+92
-2
lines changed

2 files changed

+92
-2
lines changed

src/r3_core/src/utils/alloc/freeze.rs

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
//! This module deals with interior mutability, which [undermines][1] the
2+
//! soundness of runtime uses of `const`-allocated heap objects.
3+
//!
4+
//! [1]: https://github.com/rust-lang/rust/pull/91884#discussion_r774659436
5+
use core::fmt;
6+
7+
// FIXME: Get rid of `Frozen` if one of the folllowing things happens:
8+
// (1) It's decided that interior mutability implies `!Copy`.
9+
// (2) A trait indicating the absence of interior mutability, such as
10+
// the one proposed by the now-closed [RFC 2944], is added to `core`.
11+
//
12+
// [RFC 2944]: https://github.com/rust-lang/rfcs/pull/2944
13+
//
14+
// <https://github.com/rust-lang/rust/issues/25053#issuecomment-493742957>:
15+
//
16+
// > Basically, currently we have no interior-mutable-`Copy` type, but that is
17+
// > an accident. And we also have legit needs for `Copy` interior mutable
18+
// > types, which is irreconcilable with using `Copy` to exclude interior
19+
// > mutability.
20+
21+
/// Erases interior mutability by preventing reference forming.
22+
#[repr(transparent)]
23+
pub struct Frozen<T: ?Sized>(T);
24+
25+
impl<T: Copy> Frozen<T> {
26+
/// Get a copy of the contained `T`.
27+
#[inline]
28+
pub const fn get(&self) -> T {
29+
self.0
30+
}
31+
32+
/// Copy the referenced `[T]` to the CTFE heap. The resulting reference can
33+
/// be safely consumed at runtime.
34+
///
35+
/// # Example
36+
///
37+
/// ```rust
38+
/// use r3_core::utils::Frozen;
39+
/// const SLICE: &[Frozen<u8>] = Frozen::leak_slice(&[1, 2, 3]);
40+
/// assert_eq!(SLICE[1].get(), 2);
41+
/// ```
42+
pub const fn leak_slice<'out>(x: &[T]) -> &'out [Frozen<T>]
43+
where
44+
T: 'out,
45+
{
46+
let size = core::mem::size_of::<T>()
47+
.checked_mul(x.len())
48+
.expect("size overflow");
49+
let align = core::mem::align_of::<T>();
50+
unsafe {
51+
// Allocate a CTFE heap memory block
52+
let ptr = core::intrinsics::const_allocate(size, align).cast::<T>();
53+
assert!(
54+
!ptr.guaranteed_eq(core::ptr::null_mut()),
55+
"heap allocation failed"
56+
);
57+
58+
// Copy the `[T]` onto it
59+
core::ptr::copy_nonoverlapping(x.as_ptr(), ptr, x.len());
60+
61+
// Reinterpret it as `[Frozen<T>]` (it's safe because of
62+
// `repr(transparent)`)
63+
let ptr = ptr.cast::<Frozen<T>>();
64+
65+
// Turn `ptr` into a alice reference
66+
core::slice::from_raw_parts(ptr, x.len())
67+
}
68+
}
69+
}
70+
71+
impl<T: Copy> Copy for Frozen<T> {}
72+
73+
impl<T: Copy> const Clone for Frozen<T> {
74+
#[inline]
75+
fn clone(&self) -> Self {
76+
// Don't use `T as Clone` because it could expose interior mutability.
77+
*self
78+
}
79+
80+
#[inline]
81+
fn clone_from(&mut self, source: &Self) {
82+
*self = *source;
83+
}
84+
}
85+
86+
impl<T: Copy + ~const fmt::Debug> const fmt::Debug for Frozen<T> {
87+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
88+
self.get().fmt(f)
89+
}
90+
}

src/r3_core/src/utils/alloc/mod.rs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
//! Compile-time memory allocation
22
#[macro_use]
33
mod vec;
4-
pub use vec::*;
54
mod allocator;
5+
mod freeze;
66
mod rlsf;
7-
pub use allocator::*;
7+
pub use {allocator::*, freeze::*, vec::*};

0 commit comments

Comments
 (0)