Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions libs/Patches.md
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,13 @@ into the main commit for that patch, and then the *Update* line can be removed.
* We use the Wasm configuration for the internal `guard::enable` function,
which simply leaks everything.

* Avoid unsupported pointer-cast in `std::slice::as_chunks_unchecked` (last applied Feb 26, 2026)

Due to limitations of the current memory model we need to allocate a new
array of arrays and then shallow-copy the original elements of the slice
into those arrays. We note that this is unsound in the face of interior
mutability and it leaks memory.

# Notes

This section contains more detailed notes about why certain patches are written
Expand Down
22 changes: 22 additions & 0 deletions libs/core/src/crucible/alloc.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
#![expect(unused_variables)]

// These functions have been moved temporarily in support
// of core::slice::as_chunks_unchecked. They should be
// returned to crucible::alloc once they are no longer needed.

/// Allocate an array of `len` elements of type `T`. The array begins uninitialized.
pub fn allocate<T>(len: usize) -> *mut T {
unimplemented!("allocate")
}

/// Allocate an array of `len` elements of type `T`. The array initially contains all zeros. This
/// fails if `crux-mir` doesn't know how to zero-initialize `T`.
pub fn allocate_zeroed<T>(len: usize) -> *mut T {
unimplemented!("allocate_zeroed")
}

/// Reallocate the array at `*ptr` to contain `new_len` elements. This reallocation always happens
/// in-place and never fails, so there is no need to return a new pointer.
pub fn reallocate<T>(ptr: *mut T, new_len: usize) {
unimplemented!("reallocate")
}
3 changes: 3 additions & 0 deletions libs/core/src/crucible/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,6 @@ pub mod ptr;

#[unstable(feature = "crucible_intrinsics", issue = "none")]
pub mod concurrency;

#[unstable(feature = "crucible_intrinsics", issue = "none")]
pub mod alloc;
33 changes: 27 additions & 6 deletions libs/core/src/slice/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
#![stable(feature = "rust1", since = "1.0.0")]

use crate::cmp::Ordering::{self, Equal, Greater, Less};
use crate::intrinsics::{exact_div, unchecked_sub};
use crate::intrinsics::{const_eval_select, exact_div, unchecked_sub};
use crate::mem::{self, MaybeUninit, SizedTypeProperties};
use crate::num::NonZero;
use crate::ops::{OneSidedRange, OneSidedRangeBound, Range, RangeBounds, RangeInclusive};
Expand Down Expand Up @@ -1332,6 +1332,7 @@ impl<T> [T] {
/// ```
#[stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_const_stable(feature = "slice_as_chunks", since = "1.88.0")]
#[rustc_allow_const_fn_unstable(const_eval_select)]
#[inline]
#[must_use]
#[track_caller]
Expand All @@ -1341,11 +1342,31 @@ impl<T> [T] {
"slice::as_chunks_unchecked requires `N != 0` and the slice to split exactly into `N`-element chunks",
(n: usize = N, len: usize = self.len()) => n != 0 && len.is_multiple_of(n),
);
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
let new_len = unsafe { exact_div(self.len(), N) };
// SAFETY: We cast a slice of `new_len * N` elements into
// a slice of `new_len` many `N` elements chunks.
unsafe { from_raw_parts(self.as_ptr().cast(), new_len) }

const fn const_case<T, const N: usize>(xs: &[T]) -> &[[T; N]] {
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
let new_len = unsafe { exact_div(xs.len(), N) };

// SAFETY: We cast a slice of `new_len * N` elements into
// a slice of `new_len` many `N` elements chunks.
unsafe { from_raw_parts(xs.as_ptr().cast(), new_len) }
}

// The non-const case needs to be able to call allocate which is not const fn
fn mut_case<T, const N: usize>(xs: &[T]) -> &[[T; N]] {
// SAFETY: Caller must guarantee that `N` is nonzero and exactly divides the slice length
let new_len = unsafe { exact_div(xs.len(), N) };

let ptr = crate::crucible::alloc::allocate::<[T; N]>(new_len);
for i in 0..new_len {
unsafe {
ptr.add(i).write(crate::array::from_fn(|j| xs.as_ptr().add(N*i+j).read()));
}
}
unsafe { from_raw_parts(ptr, new_len) }
}

const_eval_select((self,), const_case, mut_case)
}

/// Splits the slice into a slice of `N`-element arrays,
Expand Down
7 changes: 3 additions & 4 deletions libs/crucible/alloc.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,22 +5,21 @@ use core::ptr::NonNull;

/// Allocate an array of `len` elements of type `T`. The array begins uninitialized.
pub fn allocate<T>(len: usize) -> *mut T {
unimplemented!("allocate")
core::crucible::alloc::allocate(len)
}

/// Allocate an array of `len` elements of type `T`. The array initially contains all zeros. This
/// fails if `crux-mir` doesn't know how to zero-initialize `T`.
pub fn allocate_zeroed<T>(len: usize) -> *mut T {
unimplemented!("allocate_zeroed")
core::crucible::alloc::allocate_zeroed(len)
}

/// Reallocate the array at `*ptr` to contain `new_len` elements. This reallocation always happens
/// in-place and never fails, so there is no need to return a new pointer.
pub fn reallocate<T>(ptr: *mut T, new_len: usize) {
unimplemented!("reallocate")
core::crucible::alloc::reallocate(ptr, new_len)
}


pub struct TypedAllocator<T>(pub PhantomData<T>);

impl<T> TypedAllocator<T> {
Expand Down