From 37ad99b20b283c9d68ea58d867a577b099276e8c Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Wed, 6 Dec 2023 15:17:06 -0800 Subject: [PATCH] Traits other than NoCell permit UnsafeCells TODO: - Require `T: NoCell` and `U: NoCell` in `transmute_ref!` and `transmute_mut!` - Tests - Exercise transmuting `UnsafeCell`s by value - Fix compilation with some `AsBytes` methods - Time to rename `AsBytes` to `ToBytes`? Closes ##251 --- src/lib.rs | 231 ++++++++++++++++++++++++----------------------------- 1 file changed, 104 insertions(+), 127 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index cb154024c1a..3539acfcb4a 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -273,7 +273,7 @@ pub use zerocopy_derive::KnownLayout; pub use zerocopy_derive::NoCell; use core::{ - cell::{self, RefMut}, + cell::{self, RefMut, UnsafeCell}, cmp::Ordering, fmt::{self, Debug, Display, Formatter}, hash::Hasher, @@ -982,15 +982,21 @@ impl_known_layout!(const N: usize, T => [T; N]); safety_comment! { /// SAFETY: - /// `str` and `ManuallyDrop<[T]>` [1] have the same representations as - /// `[u8]` and `[T]` repsectively. `str` has different bit validity than - /// `[u8]`, but that doesn't affect the soundness of this impl. + /// `str`, `ManuallyDrop<[T]>` [1], and `UnsafeCell` [2] have the same + /// representations as `[u8]`, `T`, and `T` repsectively. `str` has + /// different bit validity than `[u8]`, but that doesn't affect the + /// soundness of this impl. /// /// [1] Per https://doc.rust-lang.org/nightly/core/mem/struct.ManuallyDrop.html: /// /// `ManuallyDrop` is guaranteed to have the same layout and bit /// validity as `T` /// + /// [2] Per https://doc.rust-lang.org/core/cell/struct.UnsafeCell.html#memory-layout: + /// + /// `UnsafeCell` has the same in-memory representation as its inner + /// type `T`. + /// /// TODO(#429): /// - Add quotes from docs. /// - Once [1] (added in @@ -998,6 +1004,7 @@ safety_comment! { /// quote the stable docs instead of the nightly docs. unsafe_impl_known_layout!(#[repr([u8])] str); unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] ManuallyDrop); + unsafe_impl_known_layout!(T: ?Sized + KnownLayout => #[repr(T)] UnsafeCell); } /// Analyzes whether a type is [`FromZeroes`]. @@ -1051,19 +1058,12 @@ safety_comment! { /// - If the type is an enum, it must be C-like (meaning that all variants have /// no fields) and it must have a variant with a discriminant of `0`. See [the /// reference] for a description of how discriminant values are chosen. -/// - The type must not contain any [`UnsafeCell`]s (this is required in order -/// for it to be sound to construct a `&[u8]` and a `&T` to the same region of -/// memory). The type may contain references or pointers to `UnsafeCell`s so -/// long as those values can themselves be initialized from zeroes -/// (`FromZeroes` is not currently implemented for, e.g., -/// `Option<&UnsafeCell<_>>`, but it could be one day). /// /// This analysis is subject to change. Unsafe code may *only* rely on the /// documented [safety conditions] of `FromZeroes`, and must *not* rely on the /// implementation details of this derive. /// /// [the reference]: https://doc.rust-lang.org/reference/items/enumerations.html#custom-discriminant-values-for-fieldless-enumerations -/// [`UnsafeCell`]: core::cell::UnsafeCell /// /// ## Why isn't an explicit representation required for structs? /// @@ -1267,14 +1267,11 @@ pub unsafe trait TryFromBytes { /// Note that Rust's bit validity rules are still being decided. As such, /// there exist types whose bit validity is ambiguous. See the /// `TryFromBytes` docs for a discussion of how these cases are handled. - // TODO(#251): In a future in which we distinguish between `FromBytes` and - // `RefFromBytes`, this requires `where Self: RefFromBytes` to disallow - // interior mutability. #[inline] #[doc(hidden)] // TODO(#5): Finalize name before remove this attribute. fn try_from_ref(bytes: &[u8]) -> Option<&Self> where - Self: KnownLayout, + Self: KnownLayout + NoCell, { let maybe_self = Ptr::from(bytes).try_cast_into_no_leftover::()?; @@ -1283,8 +1280,7 @@ pub unsafe trait TryFromBytes { // references exist to this memory region. // - Since `[u8]` contains no `UnsafeCell`s, we know there are no // `&UnsafeCell` references to this memory region. - // - Since we don't permit implementing `TryFromBytes` for types which - // contain `UnsafeCell`s, there are no `UnsafeCell`s in `Self`, and so + // - Since `Self: NoCell`, there are no `UnsafeCell`s in `Self`, and so // the requirement that all references contain `UnsafeCell`s at the // same offsets is trivially satisfied. // - All bytes of `bytes` are initialized. @@ -1304,11 +1300,10 @@ pub unsafe trait TryFromBytes { // - Since the argument and return types are immutable references, // Rust will prevent the caller from producing any mutable // references to the same memory region. - // - Since `Self` is not allowed to contain any `UnsafeCell`s and the - // same is true of `[u8]`, interior mutation is not possible. Thus, - // no mutation is possible. For the same reason, there is no - // mismatch between the two types in terms of which byte ranges are - // referenced as `UnsafeCell`s. + // - Since `Self: NoCell` and `[u8]: NoCell`, interior mutation is not + // possible. Thus, no mutation is possible. For the same reason, + // there is no mismatch between the two types in terms of which byte + // ranges are referenced as `UnsafeCell`s. // - Since interior mutation isn't possible within `Self`, there's no // way for the returned reference to be used to modify the byte range, // and thus there's no way for the returned reference to be used to @@ -1369,16 +1364,9 @@ pub unsafe trait TryFromBytes { /// `FromZeroes` manually, and you don't plan on writing unsafe code that /// operates on `FromZeroes` types, then you don't need to read this section.* /// -/// If `T: FromZeroes`, then unsafe code may assume that: -/// - It is sound to treat any initialized sequence of zero bytes of length -/// `size_of::()` as a `T`. -/// - Given `b: &[u8]` where `b.len() == size_of::()`, `b` is aligned to -/// `align_of::()`, and `b` contains only zero bytes, it is sound to -/// construct a `t: &T` at the same address as `b`, and it is sound for both -/// `b` and `t` to be live at the same time. -/// -/// If a type is marked as `FromZeroes` which violates this contract, it may -/// cause undefined behavior. +/// If `T: FromZeroes`, then unsafe code may assume that it is sound to produce +/// a `T` whose bytes are all initialized to zero. If a type is marked as +/// `FromZeroes` which violates this contract, it may cause undefined behavior. /// /// `#[derive(FromZeroes)]` only permits [types which satisfy these /// requirements][derive-analysis]. @@ -1703,14 +1691,6 @@ pub unsafe trait FromZeroes { /// bit pattern is a valid one). Be very careful when using the `C`, /// `usize`, or `isize` representations, as their size is /// platform-dependent. -/// - The type must not contain any [`UnsafeCell`]s (this is required in order -/// for it to be sound to construct a `&[u8]` and a `&T` to the same region of -/// memory). The type may contain references or pointers to `UnsafeCell`s so -/// long as those values can themselves be initialized from zeroes -/// (`FromBytes` is not currently implemented for, e.g., `Option<*const -/// UnsafeCell<_>>`, but it could be one day). -/// -/// [`UnsafeCell`]: core::cell::UnsafeCell /// /// This analysis is subject to change. Unsafe code may *only* rely on the /// documented [safety conditions] of `FromBytes`, and must *not* rely on the @@ -1814,19 +1794,13 @@ pub use zerocopy_derive::FromBytes; /// `FromBytes` manually, and you don't plan on writing unsafe code that /// operates on `FromBytes` types, then you don't need to read this section.* /// -/// If `T: FromBytes`, then unsafe code may assume that: -/// - It is sound to treat any initialized sequence of bytes of length -/// `size_of::()` as a `T`. -/// - Given `b: &[u8]` where `b.len() == size_of::()`, `b` is aligned to -/// `align_of::()` it is sound to construct a `t: &T` at the same address -/// as `b`, and it is sound for both `b` and `t` to be live at the same time. -/// -/// If a type is marked as `FromBytes` which violates this contract, it may -/// cause undefined behavior. +/// If `T: FromBytes`, then unsafe code may assume that it is sound to produce a +/// `T` whose bytes are initialized to any sequence of valid `u8`s (in other +/// words, any byte value which is not uninitialized). If a type is marked as +/// `FromZeroes` which violates this contract, it may cause undefined behavior. /// /// `#[derive(FromBytes)]` only permits [types which satisfy these /// requirements][derive-analysis]. -/// #[cfg_attr( feature = "derive", doc = "[derive]: zerocopy_derive::FromBytes", @@ -1878,7 +1852,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn ref_from(bytes: &[u8]) -> Option<&Self> where - Self: Sized, + Self: NoCell + Sized, { Ref::<&[u8], Self>::new(bytes).map(Ref::into_ref) } @@ -1920,7 +1894,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn ref_from_prefix(bytes: &[u8]) -> Option<&Self> where - Self: Sized, + Self: Sized + NoCell, { Ref::<&[u8], Self>::new_from_prefix(bytes).map(|(r, _)| r.into_ref()) } @@ -1956,7 +1930,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn ref_from_suffix(bytes: &[u8]) -> Option<&Self> where - Self: Sized, + Self: Sized + NoCell, { Ref::<&[u8], Self>::new_from_suffix(bytes).map(|(_, r)| r.into_ref()) } @@ -1998,7 +1972,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn mut_from(bytes: &mut [u8]) -> Option<&mut Self> where - Self: Sized + AsBytes, + Self: Sized + AsBytes + NoCell, { Ref::<&mut [u8], Self>::new(bytes).map(Ref::into_mut) } @@ -2045,7 +2019,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn mut_from_prefix(bytes: &mut [u8]) -> Option<&mut Self> where - Self: Sized + AsBytes, + Self: Sized + AsBytes + NoCell, { Ref::<&mut [u8], Self>::new_from_prefix(bytes).map(|(r, _)| r.into_mut()) } @@ -2085,7 +2059,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn mut_from_suffix(bytes: &mut [u8]) -> Option<&mut Self> where - Self: Sized + AsBytes, + Self: Sized + AsBytes + NoCell, { Ref::<&mut [u8], Self>::new_from_suffix(bytes).map(|(_, r)| r.into_mut()) } @@ -2132,7 +2106,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn slice_from(bytes: &[u8]) -> Option<&[Self]> where - Self: Sized, + Self: Sized + NoCell, { Ref::<_, [Self]>::new_slice(bytes).map(|r| r.into_slice()) } @@ -2183,7 +2157,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn slice_from_prefix(bytes: &[u8], count: usize) -> Option<(&[Self], &[u8])> where - Self: Sized, + Self: Sized + NoCell, { Ref::<_, [Self]>::new_slice_from_prefix(bytes, count).map(|(r, b)| (r.into_slice(), b)) } @@ -2234,7 +2208,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn slice_from_suffix(bytes: &[u8], count: usize) -> Option<(&[u8], &[Self])> where - Self: Sized, + Self: Sized + NoCell, { Ref::<_, [Self]>::new_slice_from_suffix(bytes, count).map(|(b, r)| (b, r.into_slice())) } @@ -2285,7 +2259,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn mut_slice_from(bytes: &mut [u8]) -> Option<&mut [Self]> where - Self: Sized + AsBytes, + Self: Sized + AsBytes + NoCell, { Ref::<_, [Self]>::new_slice(bytes).map(|r| r.into_mut_slice()) } @@ -2340,7 +2314,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn mut_slice_from_prefix(bytes: &mut [u8], count: usize) -> Option<(&mut [Self], &mut [u8])> where - Self: Sized + AsBytes, + Self: Sized + AsBytes + NoCell, { Ref::<_, [Self]>::new_slice_from_prefix(bytes, count).map(|(r, b)| (r.into_mut_slice(), b)) } @@ -2395,7 +2369,7 @@ pub unsafe trait FromBytes: FromZeroes { #[inline] fn mut_slice_from_suffix(bytes: &mut [u8], count: usize) -> Option<(&mut [u8], &mut [Self])> where - Self: Sized + AsBytes, + Self: Sized + AsBytes + NoCell, { Ref::<_, [Self]>::new_slice_from_suffix(bytes, count).map(|(b, r)| (b, r.into_mut_slice())) } @@ -2596,14 +2570,6 @@ pub unsafe trait FromBytes: FromZeroes { /// - It must be a C-like enum (meaning that all variants have no fields). /// - It must have a defined representation (`repr`s `C`, `u8`, `u16`, `u32`, /// `u64`, `usize`, `i8`, `i16`, `i32`, `i64`, or `isize`). -/// - The type must not contain any [`UnsafeCell`]s (this is required in order -/// for it to be sound to construct a `&[u8]` and a `&T` to the same region of -/// memory). The type may contain references or pointers to `UnsafeCell`s so -/// long as those values can themselves be initialized from zeroes (`AsBytes` -/// is not currently implemented for, e.g., `Option<&UnsafeCell<_>>`, but it -/// could be one day). -/// -/// [`UnsafeCell`]: core::cell::UnsafeCell /// /// This analysis is subject to change. Unsafe code may *only* rely on the /// documented [safety conditions] of `FromBytes`, and must *not* rely on the @@ -2666,15 +2632,10 @@ pub use zerocopy_derive::AsBytes; /// `AsBytes` manually, and you don't plan on writing unsafe code that /// operates on `AsBytes` types, then you don't need to read this section.* /// -/// If `T: AsBytes`, then unsafe code may assume that: -/// - It is sound to treat any `t: T` as an immutable `[u8]` of length -/// `size_of_val(t)`. -/// - Given `t: &T`, it is sound to construct a `b: &[u8]` where `b.len() == -/// size_of_val(t)` at the same address as `t`, and it is sound for both `b` -/// and `t` to be live at the same time. -/// -/// If a type is marked as `AsBytes` which violates this contract, it may cause -/// undefined behavior. +/// If `T: AsBytes`, then unsafe code may assume that it is sound to treat any +/// `t: T` as an immutable `[u8]` of length `size_of_val(t)`. If a type is +/// marked as `AsBytes` which violates this contract, it may cause undefined +/// behavior. /// /// `#[derive(AsBytes)]` only permits [types which satisfy these /// requirements][derive-analysis]. @@ -2733,7 +2694,10 @@ pub unsafe trait AsBytes { /// assert_eq!(bytes, [0, 1, 2, 3, 4, 5, 6, 7]); /// ``` #[inline(always)] - fn as_bytes(&self) -> &[u8] { + fn as_bytes(&self) -> &[u8] + where + Self: NoCell, + { // Note that this method does not have a `Self: Sized` bound; // `size_of_val` works for unsized values too. let len = mem::size_of_val(self); @@ -2753,8 +2717,9 @@ pub unsafe trait AsBytes { // - Since `slf` is derived from `self`, and `self` is an immutable // reference, the only other references to this memory region that // could exist are other immutable references, and those don't allow - // mutation. `AsBytes` prohibits types which contain `UnsafeCell`s, - // which are the only types for which this rule wouldn't be sufficient. + // mutation. `Self: NoCell` prohibits types which contain + // `UnsafeCell`s, which are the only types for which this rule + // wouldn't be sufficient. // - The total size of the resulting slice is no larger than // `isize::MAX` because no allocation produced by safe code can be // larger than `isize::MAX`. @@ -2807,7 +2772,7 @@ pub unsafe trait AsBytes { #[inline(always)] fn as_bytes_mut(&mut self) -> &mut [u8] where - Self: FromBytes, + Self: FromBytes + NoCell, { // Note that this method does not have a `Self: Sized` bound; // `size_of_val` works for unsized values too. @@ -3373,11 +3338,8 @@ safety_comment! { safety_comment! { /// SAFETY: /// The following types can be transmuted from `[0u8; size_of::()]`. [1] - /// None of them contain `UnsafeCell`s, and so they all soundly implement - /// `FromZeroes`. /// - /// [1] Per - /// https://doc.rust-lang.org/nightly/core/option/index.html#representation: + /// [1] Per https://doc.rust-lang.org/nightly/core/option/index.html#representation: /// /// Rust guarantees to optimize the following types `T` such that /// [`Option`] has the same size and alignment as `T`. In some of these @@ -3407,6 +3369,13 @@ safety_comment! { unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => FromZeroes for opt_extern_c_fn!(...)); } +safety_comment! { + /// SAFETY: + /// TODO + unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => NoCell for opt_fn!(...)); + unsafe_impl_for_power_set!(A, B, C, D, E, F, G, H, I, J, K, L -> M => NoCell for opt_extern_c_fn!(...)); +} + safety_comment! { /// SAFETY: /// Per reference [1]: @@ -3455,14 +3424,12 @@ safety_comment! { /// its argument contains a valid `Wrapping`. /// - `FromBytes`: Since `Wrapping` has the same bit validity as `T`, if /// `T: FromBytes`, then all initialized byte sequences are valid - /// instances of `Wrapping`. Similarly, if `T: FromBytes`, then - /// `Wrapping` doesn't contain any `UnsafeCell`s. Thus, `impl FromBytes - /// for Wrapping where T: FromBytes` is a sound impl. + /// instances of `Wrapping`. Thus, `impl FromBytes for Wrapping + /// where T: FromBytes` is a sound impl. /// - `AsBytes`: Since `Wrapping` has the same bit validity as `T`, if /// `T: AsBytes`, then all valid instances of `Wrapping` have all of - /// their bytes initialized. Similarly, if `T: AsBytes`, then - /// `Wrapping` doesn't contain any `UnsafeCell`s. Thus, `impl AsBytes - /// for Wrapping where T: AsBytes` is a valid impl. + /// their bytes initialized. Thus, `impl AsBytes for Wrapping where T: + /// AsBytes` is a valid impl. /// - `Unaligned`: Since `Wrapping` has the same layout as `T`, /// `Wrapping` has alignment 1 exactly when `T` does. /// @@ -3504,25 +3471,15 @@ safety_comment! { /// - `NoCell`: `MaybeUninit` has `UnsafeCell`s exactly when `T` does, so /// `T: NoCell` guarantees that `MaybeUninit` has no `UnsafeCell`s. /// - `TryFromBytes` (with no validator), `FromZeroes`, `FromBytes`: - /// `MaybeUninit` has no restrictions on its contents. Unfortunately, - /// in addition to bit validity, `TryFromBytes`, `FromZeroes` and - /// `FromBytes` also require that implementers contain no `UnsafeCell`s. - /// Thus, we require `T: Trait` in order to ensure that `T` - and thus - /// `MaybeUninit` - contains to `UnsafeCell`s. Thus, requiring that `T` - /// implement each of these traits is sufficient. + /// `MaybeUninit` has no restrictions on its contents. /// - `Unaligned`: "MaybeUninit is guaranteed to have the same size, /// alignment, and ABI as T" [1] /// /// [1] https://doc.rust-lang.org/stable/core/mem/union.MaybeUninit.html#layout-1 - /// - /// TODO(https://github.com/google/zerocopy/issues/251): If we split - /// `FromBytes` and `RefFromBytes`, or if we introduce a separate - /// `NoCell`/`Freeze` trait, we can relax the trait bounds for `FromZeroes` - /// and `FromBytes`. unsafe_impl!(T: NoCell => NoCell for MaybeUninit); - unsafe_impl!(T: TryFromBytes => TryFromBytes for MaybeUninit); - unsafe_impl!(T: FromZeroes => FromZeroes for MaybeUninit); - unsafe_impl!(T: FromBytes => FromBytes for MaybeUninit); + unsafe_impl!(T => TryFromBytes for MaybeUninit); + unsafe_impl!(T => FromZeroes for MaybeUninit); + unsafe_impl!(T => FromBytes for MaybeUninit); unsafe_impl!(T: Unaligned => Unaligned for MaybeUninit); assert_unaligned!(MaybeUninit<()>, MaybeUninit); } @@ -3564,6 +3521,26 @@ safety_comment! { unsafe_impl!(T: ?Sized + Unaligned => Unaligned for ManuallyDrop); assert_unaligned!(ManuallyDrop<()>, ManuallyDrop); } +safety_comment! { + /// SAFETY: + /// - `FromZeroes`, `FromBytes`, `AsBytes`: `UnsafeCell` has the same bit + /// validity as `T` [1], and so implements these traits exactly when `T` + /// does. + /// - `Unaligned`: `UnsafeCell` has the same representation as `T`, and + /// so has alignment 1 exactly when `T` does. [2] + /// + /// [1] TODO(#429): Justify this claim. + /// + /// [2] Per https://doc.rust-lang.org/core/cell/struct.UnsafeCell.html#memory-layout: + /// + /// `UnsafeCell`` has the same in-memory representation as its inner + /// type `T`. + unsafe_impl!(T: ?Sized + FromZeroes => FromZeroes for UnsafeCell); + unsafe_impl!(T: ?Sized + FromBytes => FromBytes for UnsafeCell); + unsafe_impl!(T: ?Sized + AsBytes => AsBytes for UnsafeCell); + unsafe_impl!(T: ?Sized + Unaligned => Unaligned for UnsafeCell); + assert_unaligned!(UnsafeCell<()>, UnsafeCell); +} safety_comment! { /// SAFETY: /// Per the reference [1]: @@ -4722,7 +4699,7 @@ where impl<'a, B, T> Ref where B: 'a + ByteSlice, - T: FromBytes, + T: FromBytes + NoCell, { /// Converts this `Ref` into a reference. /// @@ -4742,7 +4719,7 @@ where impl<'a, B, T> Ref where B: 'a + ByteSliceMut, - T: FromBytes + AsBytes, + T: FromBytes + AsBytes + NoCell, { /// Converts this `Ref` into a mutable reference. /// @@ -4762,7 +4739,7 @@ where impl<'a, B, T> Ref where B: 'a + ByteSlice, - T: FromBytes, + T: FromBytes + NoCell, { /// Converts this `Ref` into a slice reference. /// @@ -4782,7 +4759,7 @@ where impl<'a, B, T> Ref where B: 'a + ByteSliceMut, - T: FromBytes + AsBytes, + T: FromBytes + AsBytes + NoCell, { /// Converts this `Ref` into a mutable slice reference. /// @@ -4804,7 +4781,7 @@ where impl Ref where B: ByteSlice, - T: FromBytes, + T: FromBytes + NoCell, { /// Creates an immutable reference to `T` with a specific lifetime. /// @@ -4829,7 +4806,7 @@ where impl Ref where B: ByteSliceMut, - T: FromBytes + AsBytes, + T: FromBytes + AsBytes + NoCell, { /// Creates a mutable reference to `T` with a specific lifetime. /// @@ -4854,7 +4831,7 @@ where impl Ref where B: ByteSlice, - T: FromBytes, + T: FromBytes + NoCell, { /// Creates an immutable reference to `[T]` with a specific lifetime. /// @@ -4883,7 +4860,7 @@ where impl Ref where B: ByteSliceMut, - T: FromBytes + AsBytes, + T: FromBytes + AsBytes + NoCell, { /// Creates a mutable reference to `[T]` with a specific lifetime. /// @@ -4970,7 +4947,7 @@ where impl Deref for Ref where B: ByteSlice, - T: FromBytes, + T: FromBytes + NoCell, { type Target = T; #[inline] @@ -4988,7 +4965,7 @@ where impl DerefMut for Ref where B: ByteSliceMut, - T: FromBytes + AsBytes, + T: FromBytes + AsBytes + NoCell, { #[inline] fn deref_mut(&mut self) -> &mut T { @@ -5005,7 +4982,7 @@ where impl Deref for Ref where B: ByteSlice, - T: FromBytes, + T: FromBytes + NoCell, { type Target = [T]; #[inline] @@ -5023,7 +5000,7 @@ where impl DerefMut for Ref where B: ByteSliceMut, - T: FromBytes + AsBytes, + T: FromBytes + AsBytes + NoCell, { #[inline] fn deref_mut(&mut self) -> &mut [T] { @@ -5040,7 +5017,7 @@ where impl Display for Ref where B: ByteSlice, - T: FromBytes + Display, + T: FromBytes + Display + NoCell, { #[inline] fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { @@ -5052,7 +5029,7 @@ where impl Display for Ref where B: ByteSlice, - T: FromBytes, + T: FromBytes + NoCell, [T]: Display, { #[inline] @@ -5065,7 +5042,7 @@ where impl Debug for Ref where B: ByteSlice, - T: FromBytes + Debug, + T: FromBytes + Debug + NoCell, { #[inline] fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { @@ -5077,7 +5054,7 @@ where impl Debug for Ref where B: ByteSlice, - T: FromBytes + Debug, + T: FromBytes + Debug + NoCell, { #[inline] fn fmt(&self, fmt: &mut Formatter<'_>) -> fmt::Result { @@ -5125,7 +5102,7 @@ where impl Ord for Ref where B: ByteSlice, - T: FromBytes + Ord, + T: FromBytes + Ord + NoCell, { #[inline] fn cmp(&self, other: &Self) -> Ordering { @@ -5138,7 +5115,7 @@ where impl Ord for Ref where B: ByteSlice, - T: FromBytes + Ord, + T: FromBytes + Ord + NoCell, { #[inline] fn cmp(&self, other: &Self) -> Ordering { @@ -5151,7 +5128,7 @@ where impl PartialOrd for Ref where B: ByteSlice, - T: FromBytes + PartialOrd, + T: FromBytes + PartialOrd + NoCell, { #[inline] fn partial_cmp(&self, other: &Self) -> Option { @@ -5164,7 +5141,7 @@ where impl PartialOrd for Ref where B: ByteSlice, - T: FromBytes + PartialOrd, + T: FromBytes + PartialOrd + NoCell, { #[inline] fn partial_cmp(&self, other: &Self) -> Option {