Skip to content
Open
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
140 changes: 73 additions & 67 deletions src/impls.rs
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ const _: () = unsafe {
*byte.unaligned_as_ref() < 2
})
};
impl_size_eq!(bool, u8);
impl_size_eq!(=> bool, u8);

// SAFETY:
// - `Immutable`: `char` self-evidently does not contain any `UnsafeCell`s.
Expand Down Expand Up @@ -144,7 +144,7 @@ const _: () = unsafe {
});
};

impl_size_eq!(char, Unalign<u32>);
impl_size_eq!(=> char, Unalign<u32>);

// SAFETY: Per the Reference [1], `str` has the same layout as `[u8]`.
// - `Immutable`: `[u8]` does not contain any `UnsafeCell`s.
Expand Down Expand Up @@ -179,13 +179,13 @@ const _: () = unsafe {
})
};

impl_size_eq!(str, [u8]);
impl_size_eq!(=> str, [u8]);

macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
($($nonzero:ident[$prim:ty]),*) => {
$(
unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
impl_size_eq!($nonzero, Unalign<$prim>);
impl_size_eq!(=> $nonzero, Unalign<$prim>);

let n = n.transmute::<Unalign<$prim>, invariant::Valid, _>();
$nonzero::new(n.read_unaligned().into_inner()).is_some()
Expand Down Expand Up @@ -403,13 +403,23 @@ const _: () = unsafe {
mod atomics {
use super::*;

macro_rules! impl_traits_for_atomics {
($($atomics:ident [$primitives:ident]),* $(,)?) => {
macro_rules! impl_layout_traits_for_atomics {
($($($tyvar:ident)? => $atomics:ty [$primitives:ty]),* $(,)?) => {
$(
impl_known_layout!($($tyvar => )? $atomics);

impl_size_eq!($($tyvar)? => $atomics, $primitives);
impl_size_eq!($($tyvar)? => $atomics, UnsafeCell<$primitives>);
)*
};
}

macro_rules! impl_validity_traits_for_atomics {
($($atomics:tt [$primitives:ty]),* $(,)?) => {
$(
impl_known_layout!($atomics);
impl_for_transmute_from!(=> TryFromBytes for $atomics [UnsafeCell<$primitives>]);
impl_for_transmute_from!(=> FromZeros for $atomics [UnsafeCell<$primitives>]);
impl_for_transmute_from!(=> FromBytes for $atomics [UnsafeCell<$primitives>]);
impl_for_transmute_from!(=> TryFromBytes for $atomics [UnsafeCell<$primitives>]);
impl_for_transmute_from!(=> IntoBytes for $atomics [UnsafeCell<$primitives>]);
)*
};
Expand All @@ -425,8 +435,7 @@ mod atomics {
($($($tyvar:ident)? => $atomic:ty [$prim:ty]),*) => {{
crate::util::macros::__unsafe();

use core::cell::UnsafeCell;
use crate::pointer::{SizeEq, TransmuteFrom, invariant::Valid};
use crate::pointer::{TransmuteFrom, invariant::Valid};

$(
// SAFETY: The caller promised that `$atomic` and `$prim` have
Expand All @@ -436,30 +445,18 @@ mod atomics {
// the same size and bit validity.
unsafe impl<$($tyvar)?> TransmuteFrom<$prim, Valid, Valid> for $atomic {}

// SAFETY: The caller promised that `$atomic` and `$prim` have
// the same size.
unsafe impl<$($tyvar)?> SizeEq<$atomic> for $prim {
type CastFrom = $crate::pointer::cast::CastSized;
}
// SAFETY: See previous safety comment.
unsafe impl<$($tyvar)?> SizeEq<$prim> for $atomic {
type CastFrom = $crate::pointer::cast::CastSized;
}
// SAFETY: The caller promised that `$atomic` and `$prim` have
// the same size. `UnsafeCell<T>` has the same size as `T` [1].
//
// [1] Per https://doc.rust-lang.org/1.85.0/std/cell/struct.UnsafeCell.html#memory-layout:
//
// `UnsafeCell<T>` has the same in-memory representation as
// its inner type `T`. A consequence of this guarantee is that
// it is possible to convert between `T` and `UnsafeCell<T>`.
unsafe impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> {
type CastFrom = $crate::pointer::cast::CastSized;
}
// SAFETY: See previous safety comment.
unsafe impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
type CastFrom = $crate::pointer::cast::CastSized;
}
// impl<$($tyvar)?> SizeEq<$atomic> for $prim {
// type CastFrom = $crate::pointer::cast::CastSizedExact;
// }
// impl<$($tyvar)?> SizeEq<$prim> for $atomic {
// type CastFrom = $crate::pointer::cast::CastSizedExact;
// }
// impl<$($tyvar)?> SizeEq<$atomic> for UnsafeCell<$prim> {
// type CastFrom = $crate::pointer::cast::CastSizedExact;
// }
// impl<$($tyvar)?> SizeEq<UnsafeCell<$prim>> for $atomic {
// type CastFrom = $crate::pointer::cast::CastSizedExact;
// }

// SAFETY: The caller promised that `$atomic` and `$prim` have
// the same bit validity. `UnsafeCell<T>` has the same bit
Expand All @@ -484,12 +481,11 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicU8[u8], AtomicI8[i8]);
impl_layout_traits_for_atomics!(=> AtomicBool [bool], => AtomicU8[u8], => AtomicI8[i8]);
impl_validity_traits_for_atomics!(AtomicU8[u8], AtomicI8[i8]);

impl_known_layout!(AtomicBool);

impl_for_transmute_from!(=> TryFromBytes for AtomicBool [UnsafeCell<bool>]);
impl_for_transmute_from!(=> FromZeros for AtomicBool [UnsafeCell<bool>]);
impl_for_transmute_from!(=> TryFromBytes for AtomicBool [UnsafeCell<bool>]);
impl_for_transmute_from!(=> IntoBytes for AtomicBool [UnsafeCell<bool>]);

// SAFETY: Per [1], `AtomicBool`, `AtomicU8`, and `AtomicI8` have the
Expand Down Expand Up @@ -551,7 +547,8 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]);
impl_layout_traits_for_atomics!(=> AtomicU16[u16], => AtomicI16[i16]);
impl_validity_traits_for_atomics!(AtomicU16[u16], AtomicI16[i16]);

// SAFETY: `AtomicU16` and `AtomicI16` have the same size and bit
// validity as `u16` and `i16` respectively [1][2].
Expand All @@ -578,7 +575,8 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]);
impl_layout_traits_for_atomics!(=> AtomicU32[u32], => AtomicI32[i32]);
impl_validity_traits_for_atomics!(AtomicU32[u32], AtomicI32[i32]);

// SAFETY: `AtomicU32` and `AtomicI32` have the same size and bit
// validity as `u32` and `i32` respectively [1][2].
Expand All @@ -605,7 +603,8 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]);
impl_layout_traits_for_atomics!(=> AtomicU64[u64], => AtomicI64[i64]);
impl_validity_traits_for_atomics!(AtomicU64[u64], AtomicI64[i64]);

// SAFETY: `AtomicU64` and `AtomicI64` have the same size and bit
// validity as `u64` and `i64` respectively [1][2].
Expand All @@ -632,9 +631,8 @@ mod atomics {

use super::*;

impl_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]);

impl_known_layout!(T => AtomicPtr<T>);
impl_layout_traits_for_atomics!(T => AtomicPtr<T> [*mut T], => AtomicUsize[usize], => AtomicIsize[isize]);
impl_validity_traits_for_atomics!(AtomicUsize[usize], AtomicIsize[isize]);

// FIXME(#170): Implement `FromBytes` and `IntoBytes` once we implement
// those traits for `*mut T`.
Expand Down Expand Up @@ -864,28 +862,29 @@ unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {

#[inline]
fn is_bit_valid<A: invariant::Reference>(candidate: Maybe<'_, Self, A>) -> bool {
// The only way to implement this function is using an exclusive-aliased
// pointer. `UnsafeCell`s cannot be read via shared-aliased pointers
// (other than by using `unsafe` code, which we can't use since we can't
// guarantee how our users are accessing or modifying the `UnsafeCell`).
//
// `is_bit_valid` is documented as panicking or failing to monomorphize
// if called with a shared-aliased pointer on a type containing an
// `UnsafeCell`. In practice, it will always be a monomorphization error.
// Since `is_bit_valid` is `#[doc(hidden)]` and only called directly
// from this crate, we only need to worry about our own code incorrectly
// calling `UnsafeCell::is_bit_valid`. The post-monomorphization error
// makes it easier to test that this is truly the case, and also means
// that if we make a mistake, it will cause downstream code to fail to
// compile, which will immediately surface the mistake and give us a
// chance to fix it quickly.
let c = candidate.into_exclusive_or_pme();

// SAFETY: Since `UnsafeCell<T>` and `T` have the same layout and bit
// validity, `UnsafeCell<T>` is bit-valid exactly when its wrapped `T`
// is. Thus, this is a sound implementation of
// `UnsafeCell::is_bit_valid`.
T::is_bit_valid(c.get_mut())
T::is_bit_valid(candidate.transmute())
// // The only way to implement this function is using an exclusive-aliased
// // pointer. `UnsafeCell`s cannot be read via shared-aliased pointers
// // (other than by using `unsafe` code, which we can't use since we can't
// // guarantee how our users are accessing or modifying the `UnsafeCell`).
// //
// // `is_bit_valid` is documented as panicking or failing to monomorphize
// // if called with a shared-aliased pointer on a type containing an
// // `UnsafeCell`. In practice, it will always be a monomorphization error.
// // Since `is_bit_valid` is `#[doc(hidden)]` and only called directly
// // from this crate, we only need to worry about our own code incorrectly
// // calling `UnsafeCell::is_bit_valid`. The post-monomorphization error
// // makes it easier to test that this is truly the case, and also means
// // that if we make a mistake, it will cause downstream code to fail to
// // compile, which will immediately surface the mistake and give us a
// // chance to fix it quickly.
// let c = candidate.into_exclusive_or_pme();

// // SAFETY: Since `UnsafeCell<T>` and `T` have the same layout and bit
// // validity, `UnsafeCell<T>` is bit-valid exactly when its wrapped `T`
// // is. Thus, this is a sound implementation of
// // `UnsafeCell::is_bit_valid`.
// T::is_bit_valid(c.get_mut())
}
}

Expand Down Expand Up @@ -914,11 +913,15 @@ unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {
const _: () = unsafe {
unsafe_impl!(const N: usize, T: Immutable => Immutable for [T; N]);
unsafe_impl!(const N: usize, T: TryFromBytes => TryFromBytes for [T; N]; |c| {
let c: Ptr<'_, [ReadOnly<T>; N], _> = c.cast::<_, crate::pointer::cast::CastSized, _>();
let c: Ptr<'_, [ReadOnly<T>], _> = c.as_slice();
let c: Ptr<'_, ReadOnly<[T]>, _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>();

// Note that this call may panic, but it would still be sound even if it
// did. `is_bit_valid` does not promise that it will not panic (in fact,
// it explicitly warns that it's a possibility), and we have not
// violated any safety invariants that we must fix before returning.
<[T] as TryFromBytes>::is_bit_valid(c.as_slice())
<[T] as TryFromBytes>::is_bit_valid(c)
});
unsafe_impl!(const N: usize, T: FromZeros => FromZeros for [T; N]);
unsafe_impl!(const N: usize, T: FromBytes => FromBytes for [T; N]);
Expand All @@ -927,6 +930,8 @@ const _: () = unsafe {
assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]);
unsafe_impl!(T: Immutable => Immutable for [T]);
unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c| {
let c: Ptr<'_, [ReadOnly<T>], _> = c.cast::<_, crate::pointer::cast::CastUnsized, _>();

// SAFETY: Per the reference [1]:
//
// An array of `[T; N]` has a size of `size_of::<T>() * N` and the
Expand Down Expand Up @@ -1158,6 +1163,7 @@ mod tests {
use super::*;
use crate::pointer::invariant;

#[cfg(never)]
#[test]
fn test_impls() {
// A type that can supply test cases for testing
Expand Down
8 changes: 8 additions & 0 deletions src/layout.rs
Original file line number Diff line number Diff line change
Expand Up @@ -756,6 +756,14 @@ mod cast_from {
{
}

// SAFETY: TODO
unsafe impl<Src, Dst> crate::pointer::cast::CastExact<Src, Dst> for CastFrom<Dst>
where
Src: KnownLayout<PointerMetadata = usize> + ?Sized,
Dst: KnownLayout<PointerMetadata = usize> + ?Sized,
{
}

// SAFETY: `project` produces a pointer which refers to the same referent
// bytes as its input, or to a subset of them (see inline comments for a
// more detailed proof of this). It does this using provenance-preserving
Expand Down
6 changes: 2 additions & 4 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2974,7 +2974,7 @@ unsafe fn try_read_from<S, T: TryFromBytes>(
// via `c_ptr` so long as it is live, so we don't need to worry about the
// fact that `c_ptr` may have more restricted validity than `candidate`.
let c_ptr = unsafe { c_ptr.assume_validity::<invariant::Initialized>() };
let c_ptr = c_ptr.transmute();
let c_ptr = c_ptr.cast::<_, crate::pointer::cast::CastSized, _>();

// Since we don't have `T: KnownLayout`, we hack around that by using
// `Wrapping<T>`, which implements `KnownLayout` even if `T` doesn't.
Expand Down Expand Up @@ -4390,9 +4390,7 @@ pub unsafe trait FromBytes: FromZeros {
let source = Ptr::from_mut(source);
let maybe_slf = source.try_cast_into_no_leftover::<_, BecauseImmutable>(Some(count));
match maybe_slf {
Ok(slf) => Ok(slf
.recall_validity::<_, (_, (_, (BecauseExclusive, BecauseExclusive)))>()
.as_mut()),
Ok(slf) => Ok(slf.recall_validity::<_, (_, (_, BecauseExclusive))>().as_mut()),
Err(err) => Err(err.map_src(|s| s.as_mut())),
}
}
Expand Down
Loading
Loading