Skip to content

Commit 62a27e9

Browse files
committed
[WIP] ReadOnly + is_bit_valid
1 parent c1a3796 commit 62a27e9

File tree

7 files changed

+190
-87
lines changed

7 files changed

+190
-87
lines changed

src/impls.rs

Lines changed: 21 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ use core::{
1313
ptr::NonNull,
1414
};
1515

16+
use crate::pointer::SizeEq;
17+
1618
use super::*;
1719

1820
// SAFETY: Per the reference [1], "the unit tuple (`()`) ... is guaranteed as a
@@ -105,7 +107,8 @@ assert_unaligned!(bool);
105107
// pattern 0x01.
106108
const _: () = unsafe {
107109
unsafe_impl!(=> TryFromBytes for bool; |byte| {
108-
let byte = byte.transmute::<u8, invariant::Valid, _>();
110+
impl_size_eq!(ReadOnly<bool>, u8);
111+
let byte = byte.transmute::<u8, invariant::Valid, BecauseImmutable>();
109112
*byte.unaligned_as_ref() < 2
110113
})
111114
};
@@ -134,7 +137,8 @@ const _: () = unsafe { unsafe_impl!(char: Immutable, FromZeros, IntoBytes) };
134137
// `char`.
135138
const _: () = unsafe {
136139
unsafe_impl!(=> TryFromBytes for char; |c| {
137-
let c = c.transmute::<Unalign<u32>, invariant::Valid, _>();
140+
impl_size_eq!(ReadOnly<char>, Unalign<u32>);
141+
let c = c.transmute::<Unalign<u32>, invariant::Valid, BecauseImmutable>();
138142
let c = c.read_unaligned().into_inner();
139143
char::from_u32(c).is_some()
140144
});
@@ -167,7 +171,8 @@ const _: () = unsafe { unsafe_impl!(str: Immutable, FromZeros, IntoBytes, Unalig
167171
// Returns `Err` if the slice is not UTF-8.
168172
const _: () = unsafe {
169173
unsafe_impl!(=> TryFromBytes for str; |c| {
170-
let c = c.transmute::<[u8], invariant::Valid, _>();
174+
impl_size_eq!(ReadOnly<str>, [u8]);
175+
let c = c.transmute::<[u8], invariant::Valid, BecauseImmutable>();
171176
let c = c.unaligned_as_ref();
172177
core::str::from_utf8(c).is_ok()
173178
})
@@ -179,9 +184,10 @@ macro_rules! unsafe_impl_try_from_bytes_for_nonzero {
179184
($($nonzero:ident[$prim:ty]),*) => {
180185
$(
181186
unsafe_impl!(=> TryFromBytes for $nonzero; |n| {
182-
impl_size_eq!($nonzero, Unalign<$prim>);
187+
// impl_size_eq!($nonzero, Unalign<$prim>);
188+
impl_size_eq!(ReadOnly<$nonzero>, Unalign<$prim>);
183189

184-
let n = n.transmute::<Unalign<$prim>, invariant::Valid, _>();
190+
let n = n.transmute::<Unalign<$prim>, invariant::Valid, BecauseImmutable>();
185191
$nonzero::new(n.read_unaligned().into_inner()).is_some()
186192
});
187193
)*
@@ -804,29 +810,16 @@ unsafe impl<T: TryFromBytes + ?Sized> TryFromBytes for UnsafeCell<T> {
804810
}
805811

806812
#[inline]
807-
fn is_bit_valid<A: invariant::Reference>(candidate: Maybe<'_, Self, A>) -> bool {
808-
// The only way to implement this function is using an exclusive-aliased
809-
// pointer. `UnsafeCell`s cannot be read via shared-aliased pointers
810-
// (other than by using `unsafe` code, which we can't use since we can't
811-
// guarantee how our users are accessing or modifying the `UnsafeCell`).
812-
//
813-
// `is_bit_valid` is documented as panicking or failing to monomorphize
814-
// if called with a shared-aliased pointer on a type containing an
815-
// `UnsafeCell`. In practice, it will always be a monorphization error.
816-
// Since `is_bit_valid` is `#[doc(hidden)]` and only called directly
817-
// from this crate, we only need to worry about our own code incorrectly
818-
// calling `UnsafeCell::is_bit_valid`. The post-monomorphization error
819-
// makes it easier to test that this is truly the case, and also means
820-
// that if we make a mistake, it will cause downstream code to fail to
821-
// compile, which will immediately surface the mistake and give us a
822-
// chance to fix it quickly.
823-
let c = candidate.into_exclusive_or_pme();
824-
825-
// SAFETY: Since `UnsafeCell<T>` and `T` have the same layout and bit
826-
// validity, `UnsafeCell<T>` is bit-valid exactly when its wrapped `T`
827-
// is. Thus, this is a sound implementation of
828-
// `UnsafeCell::is_bit_valid`.
829-
T::is_bit_valid(c.get_mut())
813+
fn is_bit_valid(candidate: Maybe<'_, Self>) -> bool {
814+
impl_transitive_transmute_from!(T: ?Sized => ReadOnly<UnsafeCell<T>> => UnsafeCell<T> => T);
815+
impl_transitive_transmute_from!(T: ?Sized => ReadOnly<UnsafeCell<T>> => T => ReadOnly<T>);
816+
// impl_size_eq!(T: ?Sized => ReadOnly<UnsafeCell<T>>, ReadOnly<T>);
817+
// unsafe impl<T: ?Sized> SizeEq<ReadOnly<UnsafeCell<T>>> for ReadOnly<T> {
818+
// fn cast_from_raw(t: pointer::PtrInner<'_, ReadOnly<UnsafeCell<T>>>) -> pointer::PtrInner<'_, Self> {
819+
// todo!()
820+
// }
821+
// }
822+
T::is_bit_valid(candidate.transmute::<_, _, BecauseImmutable>())
830823
}
831824
}
832825

src/lib.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1520,7 +1520,7 @@ pub unsafe trait TryFromBytes {
15201520
/// [`UnsafeCell`]: core::cell::UnsafeCell
15211521
/// [`Shared`]: invariant::Shared
15221522
#[doc(hidden)]
1523-
fn is_bit_valid<A: invariant::Reference>(candidate: Maybe<'_, Self, A>) -> bool;
1523+
fn is_bit_valid(candidate: Maybe<'_, Self>) -> bool;
15241524

15251525
/// Attempts to interpret the given `source` as a `&Self`.
15261526
///

src/macros.rs

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -895,10 +895,7 @@ macro_rules! cryptocorrosion_derive_traits {
895895
$($field_ty: $crate::FromBytes,)*
896896
)?
897897
{
898-
fn is_bit_valid<A>(_c: $crate::Maybe<'_, Self, A>) -> bool
899-
where
900-
A: $crate::pointer::invariant::Reference
901-
{
898+
fn is_bit_valid(_c: $crate::Maybe<'_, Self>) -> bool {
902899
// SAFETY: This macro only accepts `#[repr(C)]` and
903900
// `#[repr(transparent)]` structs, and this `impl` block
904901
// requires all field types to be `FromBytes`. Thus, all
@@ -1038,10 +1035,7 @@ macro_rules! cryptocorrosion_derive_traits {
10381035
$field_ty: $crate::FromBytes,
10391036
)*
10401037
{
1041-
fn is_bit_valid<A>(_c: $crate::Maybe<'_, Self, A>) -> bool
1042-
where
1043-
A: $crate::pointer::invariant::Reference
1044-
{
1038+
fn is_bit_valid(_c: $crate::Maybe<'_, Self>) -> bool {
10451039
// SAFETY: This macro only accepts `#[repr(C)]` unions, and this
10461040
// `impl` block requires all field types to be `FromBytes`.
10471041
// Thus, all initialized byte sequences constitutes valid

src/pointer/mod.rs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ pub use {
2727
///
2828
/// [`TryFromBytes::is_bit_valid`]: crate::TryFromBytes::is_bit_valid
2929
pub type Maybe<'a, T, Aliasing = invariant::Shared, Alignment = invariant::Unaligned> =
30-
Ptr<'a, T, (Aliasing, Alignment, invariant::Initialized)>;
30+
Ptr<'a, crate::wrappers::ReadOnly<T>, (Aliasing, Alignment, invariant::Initialized)>;
3131

3232
/// Checks if the referent is zeroed.
3333
pub(crate) fn is_zeroed<T, I>(ptr: Ptr<'_, T, I>) -> bool

src/util/macro_util.rs

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -29,10 +29,9 @@ use core::{
2929

3030
use crate::{
3131
pointer::{
32-
invariant::{self, BecauseExclusive, BecauseImmutable, Invariants},
32+
invariant::{self, BecauseExclusive, BecauseImmutable, Exclusive, Invariants},
3333
BecauseInvariantsEq, InvariantsEq, SizeEq, TryTransmuteFromPtr,
34-
},
35-
FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout, Ptr, TryFromBytes, ValidityError,
34+
}, FromBytes, FromZeros, Immutable, IntoBytes, KnownLayout, Ptr, ReadOnly, TryFromBytes, ValidityError
3635
};
3736

3837
/// Projects the type of the field at `Index` in `Self`.
@@ -587,19 +586,22 @@ where
587586
// initialized.
588587
let ptr = unsafe { ptr.assume_validity::<invariant::Initialized>() };
589588

590-
// SAFETY: `MaybeUninit<T>` and `T` have the same size [1], so this cast
591-
// preserves the referent's size. This cast preserves provenance.
589+
// SAFETY: `MaybeUninit<T>` and `T` have the same size [1], and
590+
// `ReadOnly<T>` and `T` have the same size, so this cast preserves the
591+
// referent's size. This cast preserves provenance.
592592
//
593593
// [1] Per https://doc.rust-lang.org/1.81.0/std/mem/union.MaybeUninit.html#layout-1:
594594
//
595595
// `MaybeUninit<T>` is guaranteed to have the same size, alignment, and
596596
// ABI as `T`
597-
let ptr: Ptr<'_, Dst, _> = unsafe {
598-
ptr.cast_unsized(|ptr: crate::pointer::PtrInner<'_, mem::MaybeUninit<Dst>>| {
597+
let ptr: Ptr<'_, ReadOnly<Dst>, (Exclusive, _, _)> = unsafe {
598+
ptr.cast_unsized::<_, _, (_, (BecauseExclusive, _))>(|ptr: crate::pointer::PtrInner<'_, mem::MaybeUninit<Dst>>| {
599599
ptr.cast_sized()
600600
})
601601
};
602602

603+
// TODO: Replace this with `ptr.into_shared()` or something.
604+
let ptr = unsafe { ptr.assume_aliasing() };
603605
if Dst::is_bit_valid(ptr.forget_aligned()) {
604606
// SAFETY: Since `Dst::is_bit_valid`, we know that `ptr`'s referent is
605607
// bit-valid for `Dst`. `ptr` points to `mu_dst`, and no intervening

src/util/macros.rs

Lines changed: 61 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -135,15 +135,15 @@ macro_rules! unsafe_impl {
135135
fn only_derive_is_allowed_to_implement_this_trait() {}
136136

137137
#[inline]
138-
fn is_bit_valid<AA: crate::pointer::invariant::Reference>($candidate: Maybe<'_, Self, AA>) -> bool {
138+
fn is_bit_valid($candidate: Maybe<'_, Self>) -> bool {
139139
$is_bit_valid
140140
}
141141
};
142142
(@method TryFromBytes) => {
143143
#[allow(clippy::missing_inline_in_public_items)]
144144
#[cfg_attr(all(coverage_nightly, __ZEROCOPY_INTERNAL_USE_ONLY_NIGHTLY_FEATURES_IN_TESTS), coverage(off))]
145145
fn only_derive_is_allowed_to_implement_this_trait() {}
146-
#[inline(always)] fn is_bit_valid<AA: crate::pointer::invariant::Reference>(_: Maybe<'_, Self, AA>) -> bool { true }
146+
#[inline(always)] fn is_bit_valid(_: Maybe<'_, Self>) -> bool { true }
147147
};
148148
(@method $trait:ident) => {
149149
#[allow(clippy::missing_inline_in_public_items, dead_code)]
@@ -166,6 +166,13 @@ macro_rules! impl_for_transmute_from {
166166
$($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?)?
167167
=> $trait:ident for $ty:ty [$($unsafe_cell:ident)? <$repr:ty>]
168168
) => {
169+
// SAFETY: TODO
170+
const _: () = unsafe {
171+
impl_size_eq!(
172+
@inner [$($tyvar $(: $(? $optbound +)* $($bound +)*)?)?] ReadOnly<$ty>, ReadOnly<$repr>
173+
);
174+
};
175+
169176
const _: () = {
170177
$(#[$attr])*
171178
#[allow(non_local_definitions)]
@@ -218,9 +225,9 @@ macro_rules! impl_for_transmute_from {
218225
TryFromBytes for $ty:ty [UnsafeCell<$repr:ty>]
219226
) => {
220227
#[inline]
221-
fn is_bit_valid<A: crate::pointer::invariant::Reference>(candidate: Maybe<'_, Self, A>) -> bool {
222-
let c: Maybe<'_, Self, crate::pointer::invariant::Exclusive> = candidate.into_exclusive_or_pme();
223-
let c: Maybe<'_, $repr, _> = c.transmute::<_, _, (_, (_, (BecauseExclusive, BecauseExclusive)))>();
228+
fn is_bit_valid(candidate: Maybe<'_, Self>) -> bool {
229+
// let c: Maybe<'_, Self, crate::pointer::invariant::Exclusive> = candidate.into_exclusive_or_pme();
230+
let c: Maybe<'_, $repr, _> = candidate.transmute::<_, _, (_, (_, (BecauseImmutable, BecauseImmutable)))>();
224231
// SAFETY: This macro ensures that `$repr` and `Self` have the same
225232
// size and bit validity. Thus, a bit-valid instance of `$repr` is
226233
// also a bit-valid instance of `Self`.
@@ -233,11 +240,11 @@ macro_rules! impl_for_transmute_from {
233240
TryFromBytes for $ty:ty [<$repr:ty>]
234241
) => {
235242
#[inline]
236-
fn is_bit_valid<A: crate::pointer::invariant::Reference>(candidate: $crate::Maybe<'_, Self, A>) -> bool {
243+
fn is_bit_valid(candidate: $crate::Maybe<'_, Self>) -> bool {
237244
// SAFETY: This macro ensures that `$repr` and `Self` have the same
238245
// size and bit validity. Thus, a bit-valid instance of `$repr` is
239246
// also a bit-valid instance of `Self`.
240-
<$repr as TryFromBytes>::is_bit_valid(candidate.transmute())
247+
<$repr as TryFromBytes>::is_bit_valid(candidate.transmute::<_, _, BecauseImmutable>())
241248
}
242249
};
243250
(
@@ -788,44 +795,56 @@ macro_rules! impl_transitive_transmute_from {
788795

789796
#[rustfmt::skip]
790797
macro_rules! impl_size_eq {
791-
($t:ty, $u:ty) => {
792-
const _: () = {
793-
use crate::{KnownLayout, pointer::{PtrInner, SizeEq}};
794-
795-
static_assert!(=> {
796-
let t = <$t as KnownLayout>::LAYOUT;
797-
let u = <$u as KnownLayout>::LAYOUT;
798-
t.align.get() >= u.align.get() && match (t.size_info, u.size_info) {
799-
(SizeInfo::Sized { size: t }, SizeInfo::Sized { size: u }) => t == u,
800-
(
801-
SizeInfo::SliceDst(TrailingSliceLayout { offset: t_offset, elem_size: t_elem_size }),
802-
SizeInfo::SliceDst(TrailingSliceLayout { offset: u_offset, elem_size: u_elem_size })
803-
) => t_offset == u_offset && t_elem_size == u_elem_size,
804-
_ => false,
805-
}
806-
});
798+
($t:ty, $u:ty) => {
799+
const _: () = { static_assert!(=> {
800+
let t = <$t as KnownLayout>::LAYOUT;
801+
let u = <$u as KnownLayout>::LAYOUT;
802+
t.align.get() >= u.align.get() && match (t.size_info, u.size_info) {
803+
(SizeInfo::Sized { size: t }, SizeInfo::Sized { size: u }) => t == u,
804+
(
805+
SizeInfo::SliceDst(TrailingSliceLayout { offset: t_offset, elem_size: t_elem_size }),
806+
SizeInfo::SliceDst(TrailingSliceLayout { offset: u_offset, elem_size: u_elem_size })
807+
) => t_offset == u_offset && t_elem_size == u_elem_size,
808+
_ => false,
809+
}
810+
}) };
807811

808-
// SAFETY: See inline.
809-
unsafe impl SizeEq<$t> for $u {
810-
#[inline(always)]
811-
fn cast_from_raw(t: PtrInner<'_, $t>) -> PtrInner<'_, $u> {
812-
// SAFETY: We've asserted that their
813-
// `KnownLayout::LAYOUT.size_info`s are equal, and so this
814-
// cast is guaranteed to preserve address and referent size.
815-
// It trivially preserves provenance.
816-
unsafe { cast!(t) }
817-
}
812+
const _: () = unsafe { impl_size_eq!(@inner [] $t, $u); };
813+
};
814+
(
815+
$tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )? =>
816+
$t:ty, $u:ty
817+
) => {
818+
impl_size_eq!(@inner [$tyvar $(: $(? $optbound)* $($bound)* )?] $t, $u);
819+
};
820+
(
821+
@inner [$($tyvar:ident $(: $(? $optbound:ident $(+)?)* $($bound:ident $(+)?)* )?)?]
822+
$t:ty, $u:ty
823+
) => {{
824+
$crate::util::macros::__unsafe();
825+
826+
use crate::{KnownLayout, pointer::{PtrInner, SizeEq}, layout::{SizeInfo, TrailingSliceLayout}};
827+
828+
// SAFETY: See inline.
829+
unsafe impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)?> SizeEq<$t> for $u {
830+
#[inline(always)]
831+
fn cast_from_raw(t: PtrInner<'_, $t>) -> PtrInner<'_, $u> {
832+
// SAFETY: We've asserted that their
833+
// `KnownLayout::LAYOUT.size_info`s are equal, and so this
834+
// cast is guaranteed to preserve address and referent size.
835+
// It trivially preserves provenance.
836+
unsafe { cast!(t) }
818837
}
819-
// SAFETY: See previous safety comment.
820-
unsafe impl SizeEq<$u> for $t {
821-
#[inline(always)]
822-
fn cast_from_raw(u: PtrInner<'_, $u>) -> PtrInner<'_, $t> {
823-
// SAFETY: See previous safety comment.
824-
unsafe { cast!(u) }
825-
}
838+
}
839+
// SAFETY: See previous safety comment.
840+
unsafe impl<$($tyvar $(: $(? $optbound +)* $($bound +)*)?)?> SizeEq<$u> for $t {
841+
#[inline(always)]
842+
fn cast_from_raw(u: PtrInner<'_, $u>) -> PtrInner<'_, $t> {
843+
// SAFETY: See previous safety comment.
844+
unsafe { cast!(u) }
826845
}
827-
};
828-
};
846+
}
847+
}};
829848
}
830849

831850
/// Invokes `$blk` in a context in which `$src<$t>` and `$dst<$u>` implement

0 commit comments

Comments
 (0)