Skip to content

[WIP] try_transmute_{ref,mut}! #1256

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
wants to merge 2 commits into from
Closed
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
120 changes: 106 additions & 14 deletions src/macro_util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ use core::ptr::{self, NonNull};
use crate::{
pointer::{
invariant::{self, AtLeast, Invariants},
AliasingSafe, AliasingSafeReason, BecauseExclusive,
AliasingSafe, AliasingSafeReason, BecauseExclusive, BecauseImmutable,
},
IntoBytes, Ptr, TryFromBytes, ValidityError,
Immutable, IntoBytes, Ptr, TryFromBytes, ValidityError,
};

/// A compile-time check that should be one particular value.
Expand Down Expand Up @@ -417,24 +417,29 @@ pub unsafe fn transmute_mut<'dst, 'src: 'dst, Src: 'src, Dst: 'dst>(
unsafe { &mut *dst }
}

/// Is a given source a valid instance of `Self`?
/// Attempts to transmute a pointer from `Src` to `Dst`.
///
/// # Safety
///
/// Unsafe code may assume that, if `is_mut_src_valid(src)` returns true, `*src`
/// is a bit-valid instance of `Dst`, and that the size of `Src` is greater than
/// or equal to the size of `Dst`.
/// Unsafe code may assume that, if `try_transmute_ptr(src)` returns true,
/// `*src` is a bit-valid instance of `Dst`, and that the size of `Src` is
/// greater than or equal to the size of `Dst`.
///
/// # Panics
///
/// `is_src_valid` may either produce a post-monomorphization error or a panic
/// if `Dst` is bigger than `Src`. Otherwise, `is_src_valid` panics under the
/// same circumstances as [`is_bit_valid`].
/// `try_transmute_ptr` may either produce a post-monomorphization error or a
/// panic if `Dst` is bigger than `Src`. Otherwise, `try_transmute_ptr` panics
/// under the same circumstances as [`is_bit_valid`].
///
/// [`is_bit_valid`]: TryFromBytes::is_bit_valid
#[doc(hidden)]
#[inline]
fn is_src_valid<Src, Dst, I, R>(src: Ptr<'_, Src, I>) -> bool
fn try_transmute_ptr<Src, Dst, I, R>(
mut src: Ptr<'_, Src, I>,
) -> Result<
Ptr<'_, Dst, (I::Aliasing, invariant::Any, invariant::Valid)>,
ValidityError<Ptr<'_, Src, I>, Dst>,
>
where
Src: IntoBytes,
Dst: TryFromBytes + AliasingSafe<Src, I::Aliasing, R>,
Expand All @@ -459,9 +464,30 @@ where
// SAFETY: `c_ptr` is derived from `src` which is `IntoBytes`. By
// invariant on `IntoByte`s, `c_ptr`'s referent consists entirely of
// initialized bytes.
let c_ptr = unsafe { c_ptr.assume_initialized() };

Dst::is_bit_valid(c_ptr)
let mut c_ptr = unsafe { c_ptr.assume_initialized() };

c_ptr.try_into_valid().map_err(|err| {
err.map_src(|src| {
// SAFETY: TODO
let src = unsafe { src.cast_unsized(|p| p as *mut Src) };
// SAFETY: `src` is the same pointer that was passed to this method,
// which had validity `invariant::Valid`. `try_into_valid` promises not
// to modify its receiver's referent, so `src`'s referent is still a
// valid `Src`.
let src = unsafe { src.assume_valid() };

src.foobar()
})
})

// if Dst::is_bit_valid(c_ptr.reborrow()) {
// // SAFETY: TODO
// let ptr = unsafe { c_ptr.assume_valid() };

// Ok(ptr)
// } else {
// Err(src)
// }
}

/// Attempts to transmute `Src` into `Dst`.
Expand All @@ -481,7 +507,7 @@ where
Src: IntoBytes,
Dst: TryFromBytes,
{
if !is_src_valid::<Src, Dst, _, BecauseExclusive>(Ptr::from_mut(&mut src)) {
if try_transmute_ptr::<Src, Dst, _, BecauseExclusive>(Ptr::from_mut(&mut src)).is_err() {
return Err(ValidityError::new(src));
}

Expand All @@ -497,6 +523,72 @@ where
Ok(unsafe { core::mem::transmute_copy(&*src) })
}

/// Attempts to transmute `&Src` into `&Dst`.
///
/// A helper for `try_transmute_ref!`.
///
/// # Safety
///
/// The caller promises that `size_of::<Src>() == size_of::<Dst>()` and that
/// `align_of::<Src>() >= align_of::<Dst>()`.
///
/// # Panics
///
/// `try_transmute_ref` may either produce a post-monomorphization error or a
/// panic if `Dst` is bigger than `Src`. Otherwise, `try_transmute_ref` panics
/// under the same circumstances as [`is_bit_valid`].
///
/// [`is_bit_valid`]: TryFromBytes::is_bit_valid
#[inline(always)]
pub unsafe fn try_transmute_ref<Src, Dst>(src: &Src) -> Result<&Dst, ValidityError<&Src, Dst>>
where
Src: IntoBytes + Immutable,
Dst: TryFromBytes + Immutable,
{
let ptr = Ptr::from_ref(src);
try_transmute_ptr::<Src, Dst, _, BecauseImmutable>(ptr)
.map(|ptr| {
// SAFETY: TODO
let ptr = unsafe { ptr.assume_alignment::<invariant::Aligned>() };
ptr.as_ref()
})
.map_err(|src| ValidityError::new(src.as_ref()))
}

/// Attempts to transmute `&mut Src` into `&mut Dst`.
///
/// A helper for `try_transmute_mut!`.
///
/// # Safety
///
/// The caller promises that `size_of::<Src>() == size_of::<Dst>()` and that
/// `align_of::<Src>() >= align_of::<Dst>()`.
///
/// # Panics
///
/// `try_transmute_mut` may either produce a post-monomorphization error or a
/// panic if `Dst` is bigger than `Src`. Otherwise, `try_transmute_mut` panics
/// under the same circumstances as [`is_bit_valid`].
///
/// [`is_bit_valid`]: TryFromBytes::is_bit_valid
#[inline(always)]
pub unsafe fn try_transmute_mut<Src, Dst>(
src: &mut Src,
) -> Result<&mut Dst, ValidityError<&mut Src, Dst>>
where
Src: IntoBytes,
Dst: TryFromBytes,
{
let ptr = Ptr::from_mut(src);
try_transmute_ptr::<Src, Dst, _, BecauseExclusive>(ptr)
.map(|ptr| {
// SAFETY: TODO
let ptr = unsafe { ptr.assume_alignment::<invariant::Aligned>() };
ptr.as_mut()
})
.map_err(|src| ValidityError::new(src.as_mut()))
}

/// A function which emits a warning if its return value is not used.
#[must_use]
#[inline(always)]
Expand Down
15 changes: 14 additions & 1 deletion src/pointer/ptr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -159,7 +159,7 @@ macro_rules! define_system {
)]
/// This trait is implemented for such tuples, and can be used to
/// project out the components of these tuples via its associated types.
pub trait $system: sealed::Sealed {
pub trait $system: sealed::Sealed + From<($(Self::$set),*)> {
$(
$(#[$set_attr])*
type $set: $set;
Expand Down Expand Up @@ -637,6 +637,19 @@ mod _transitions {
T: 'a + ?Sized,
I: Invariants,
{
pub(crate) fn foobar<II>(self) -> Ptr<'a, T, II>
where
II: Invariants<
Aliasing = I::Aliasing,
Alignment = I::Alignment,
Validity = I::Validity,
>,
{
// SAFETY: All of the invariants in `II` are the same as those in
// `I`.
unsafe { self.assume_invariants() }
}

/// Returns a `Ptr` with [`Exclusive`] aliasing if `self` already has
/// `Exclusive` aliasing.
///
Expand Down
Loading