Skip to content

Commit d64de19

Browse files
committed
feat(utils): add ConstAllocator::with_parametric
1 parent 0b81290 commit d64de19

File tree

2 files changed

+57
-1
lines changed

2 files changed

+57
-1
lines changed

src/r3_core/src/lib.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,8 @@
22
#![feature(const_fn_trait_bound)]
33
#![feature(const_fn_fn_ptr_basics)]
44
#![feature(const_fn_floating_point_arithmetic)]
5+
#![feature(const_maybe_uninit_as_mut_ptr)]
6+
#![feature(const_ptr_read)]
57
#![feature(generic_const_exprs)]
68
#![feature(const_ptr_offset)]
79
#![feature(const_swap)]

src/r3_core/src/utils/alloc/allocator.rs

Lines changed: 55 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,10 +136,28 @@ impl ConstAllocator {
136136
/// core::mem::forget(al.clone());
137137
/// }
138138
/// ```
139+
#[inline]
139140
pub const fn with<F, R>(f: F) -> R
140141
where
141142
F: ~const FnOnce(&ConstAllocator) -> R,
142143
{
144+
Self::with_inner(f)
145+
}
146+
147+
/// The variant of [`Self::with`] that lets you pass an additional parameter
148+
/// to the closure.
149+
///
150+
/// This can be used to work around the lack of compiler support for const
151+
/// closures.
152+
#[inline]
153+
pub const fn with_parametric<P, F, R>(p: P, f: F) -> R
154+
where
155+
F: ~const FnOnce(P, &ConstAllocator) -> R,
156+
{
157+
Self::with_inner((p, f))
158+
}
159+
160+
const fn with_inner<F: ~const FnOnceConstAllocator>(f: F) -> F::Output {
143161
struct RefCountGuard(usize);
144162
impl const Drop for RefCountGuard {
145163
fn drop(&mut self) {
@@ -168,7 +186,43 @@ impl ConstAllocator {
168186
tlsf: tlsf.0.as_mut().unwrap(),
169187
};
170188

171-
f(&this)
189+
f.call(&this)
190+
}
191+
}
192+
193+
/// The trait for types accepted by [`ConstAllocator::with_inner`].
194+
trait FnOnceConstAllocator {
195+
type Output;
196+
fn call(self, allocator: &ConstAllocator) -> Self::Output;
197+
}
198+
199+
/// This implementation's `call` method simply calls the `FnOnce` receiver.
200+
impl<T: ~const FnOnce(&ConstAllocator) -> Output, Output> const FnOnceConstAllocator for T {
201+
type Output = Output;
202+
fn call(self, allocator: &ConstAllocator) -> Self::Output {
203+
self(allocator)
204+
}
205+
}
206+
207+
/// This implementation's `call` method calls the `FnOnce` receiver with an
208+
/// associated parameter value.
209+
impl<P, T: ~const FnOnce(P, &ConstAllocator) -> Output, Output> const FnOnceConstAllocator
210+
for (P, T)
211+
{
212+
type Output = Output;
213+
fn call(self, allocator: &ConstAllocator) -> Self::Output {
214+
let mut this = core::mem::MaybeUninit::new(self);
215+
// Safety: It's initialized
216+
let this = unsafe { &mut *this.as_mut_ptr() };
217+
// Safety: `md.0` and `md.1` are in `MaybeUninit`, so this will not
218+
// cause double free
219+
let param = unsafe { core::ptr::read(&this.0) };
220+
let func = unsafe { core::ptr::read(&this.1) };
221+
func(param, allocator)
222+
223+
// FIXME: The following implementation doesn't work because of
224+
// <https://github.com/rust-lang/rust/issues/86897>
225+
// (self.1)(self.0, allocator)
172226
}
173227
}
174228

0 commit comments

Comments
 (0)