diff --git a/src/lib.rs b/src/lib.rs index 2c7aad6dea..fcc5a98f37 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -3511,8 +3511,8 @@ safety_comment! { /// /// In other words, the layout of a `[T]` or `[T; N]` is a sequence of `T`s /// laid out back-to-back with no bytes in between. Therefore, `[T]` or `[T; - /// N]` are `FromZeroes`, `FromBytes`, and `AsBytes` if `T` is - /// (respectively). Furthermore, since an array/slice has "the same + /// N]` are `TryFromBytes`, `FromZeroes`, `FromBytes`, and `AsBytes` if `T` + /// is (respectively). Furthermore, since an array/slice has "the same /// alignment of `T`", `[T]` and `[T; N]` are `Unaligned` if `T` is. /// /// Note that we don't `assert_unaligned!` for slice types because @@ -3524,6 +3524,42 @@ safety_comment! { unsafe_impl!(const N: usize, T: AsBytes => AsBytes for [T; N]); unsafe_impl!(const N: usize, T: Unaligned => Unaligned for [T; N]); assert_unaligned!([(); 0], [(); 1], [u8; 0], [u8; 1]); + unsafe_impl!(T: TryFromBytes => TryFromBytes for [T]; |c: Ptr<[T]>| { + // SAFETY: Assuming the preconditions of `is_bit_valid` are satisfied, + // so too will the postcondition: that, if `is_bit_valid(candidate)` + // returns true, `*candidate` contains a valid `Self`. Per the reference + // [1]: + // + // An array of `[T; N]` has a size of `size_of::() * N` and the + // same alignment of `T`. Arrays are laid out so that the zero-based + // `nth` element of the array is offset from the start of the array by + // `n * size_of::()` bytes. + // + // ... + // + // Slices have the same layout as the section of the array they slice. + // + // In other words, the layout of a `[T] is a sequence of `T`s laid out + // back-to-back with no bytes in between. If all elements in `candidate` + // are `is_bit_valid`, so too is `candidate`. + // + // Note that any of the below calls 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. + c.iter().all(|elem| + // SAFETY: We uphold the safety contract of `is_bit_valid(elem)`, by + // precondition on the surrounding call to `is_bit_valid`. The + // memory referenced by `elem` is contained entirely within `c`, and + // satisfies the preconditions satisfied by `c`. By axiom, we assume + // that `Iterator:all` does not invalidate these preconditions + // (e.g., by writing to `elem`.) Since `elem` is derived from `c`, + // it is only possible for uninitialized bytes to occur in `elem` at + // the same bytes they occur within `c`. + unsafe { ::is_bit_valid(elem) } + ) + }); unsafe_impl!(T: FromZeroes => FromZeroes for [T]); unsafe_impl!(T: FromBytes => FromBytes for [T]); unsafe_impl!(T: AsBytes => AsBytes for [T]); @@ -7666,6 +7702,7 @@ mod tests { @failure 0xD800u32, 0xDFFFu32, 0x110000u32; str => @success "", "hello", "❤️🧡💛💚💙💜", @failure [0, 159, 146, 150]; + [u8] => @success [], [0, 1, 2]; NonZeroU8, NonZeroI8, NonZeroU16, NonZeroI16, NonZeroU32, NonZeroI32, NonZeroU64, NonZeroI64, NonZeroU128, NonZeroI128, NonZeroUsize, NonZeroIsize @@ -7675,6 +7712,9 @@ mod tests { // `0` may be any integer type with a different size or // alignment than some `NonZeroXxx` types). @failure Option::::None; + [bool] + => @success [true, false], [false, true], + @failure [2u8], [3u8], [0xFFu8], [0u8, 1u8, 2u8]; ); // Asserts that `$ty` implements any `$trait` and doesn't implement any @@ -7840,7 +7880,8 @@ mod tests { assert_impls!(Unalign: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes); assert_impls!(Unalign: Unaligned, !KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes); - assert_impls!([u8]: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes); + assert_impls!([u8]: KnownLayout, TryFromBytes, FromZeroes, FromBytes, AsBytes, Unaligned); + assert_impls!([bool]: KnownLayout, TryFromBytes, FromZeroes, AsBytes, Unaligned, !FromBytes); assert_impls!([NotZerocopy]: !KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); assert_impls!([u8; 0]: KnownLayout, FromZeroes, FromBytes, AsBytes, Unaligned, !TryFromBytes); assert_impls!([NotZerocopy; 0]: KnownLayout, !TryFromBytes, !FromZeroes, !FromBytes, !AsBytes, !Unaligned); diff --git a/src/util.rs b/src/util.rs index f018e876d7..b35cc079c1 100644 --- a/src/util.rs +++ b/src/util.rs @@ -39,15 +39,15 @@ pub(crate) mod ptr { /// [covariant]: https://doc.rust-lang.org/reference/subtyping.html pub struct Ptr<'a, T: 'a + ?Sized> { // INVARIANTS: - // - `ptr` is derived from some valid Rust allocation, `A` - // - `ptr` has the same provenance as `A` - // - `ptr` addresses a byte range which is entirely contained in `A` - // - `ptr` addresses a byte range whose length fits in an `isize` - // - `ptr` addresses a byte range which does not wrap around the address - // space - // - `ptr` is validly-aligned for `T` - // - `A` is guaranteed to live for at least `'a` - // - `T: 'a` + // 1. `ptr` is derived from some valid Rust allocation, `A` + // 2. `ptr` has the same provenance as `A` + // 3. `ptr` addresses a byte range which is entirely contained in `A` + // 4. `ptr` addresses a byte range whose length fits in an `isize` + // 5. `ptr` addresses a byte range which does not wrap around the address + // space + // 6. `ptr` is validly-aligned for `T` + // 7. `A` is guaranteed to live for at least `'a` + // 8. `T: 'a` ptr: NonNull, _lifetime: PhantomData<&'a ()>, } @@ -255,6 +255,10 @@ pub(crate) mod ptr { impl<'a, T> Ptr<'a, [T]> { /// The number of slice elements referenced by `self`. + /// + /// # Safety + /// + /// Unsafe code my rely on `len` satisfying the above contract. fn len(&self) -> usize { #[allow(clippy::as_conversions)] let slc = self.ptr.as_ptr() as *const [()]; @@ -286,6 +290,83 @@ pub(crate) mod ptr { // Nightly docs. slc.len() } + + pub(crate) fn iter(&self) -> impl Iterator> { + // TODO(#429): Once `NonNull::cast` documents that it preserves + // provenance, cite those docs. + let base = self.ptr.cast::().as_ptr(); + (0..self.len()).map(move |i| { + // TODO(https://github.com/rust-lang/rust/issues/74265): Use + // `NonNull::get_unchecked_mut`. + + // SAFETY: If the following conditions are not satisfied + // `pointer::cast` may induce Undefined Behavior [1]: + // > 1. Both the starting and resulting pointer must be either + // > in bounds or one byte past the end of the same allocated + // > object. + // > 2. The computed offset, in bytes, cannot overflow an + // > `isize`. + // > 3. The offset being in bounds cannot rely on “wrapping + // > around” the address space. That is, the + // > infinite-precision sum must fit in a `usize`. + // + // [1] https://doc.rust-lang.org/std/primitive.pointer.html#method.add + // + // We satisfy all three of these conditions here: + // 1. `base` (by invariant on `self`) points to an allocated + // object. By contract, `self.len()` accurately reflects the + // number of elements in the slice. `i` is in bounds of + // `c.len()` by construction, and so the result of this + // addition cannot overflow past the end of the allocation + // referred to by `c`. + // 2. By invariant on `Ptr`, `self` addresses a byte range whose + // length fits in an `isize`. Since `elem` is contained in + // `self`, the computed offset of `elem` must fit within + // `isize.` + // 3. By invariant on `Ptr`, `self` addresses a byte range which + // does not wrap around the address space. Since `elem` is + // contained in `self`, the computed offset of `elem` must + // wrap around the address space. + // + // TODO(#429): Once `pointer::add` documents that it preserves + // provenance, cite those docs. + let elem = unsafe { base.add(i) }; + + // SAFETY: + // - `elem` must not be null. `base` is constructed from a + // `NonNull` pointer, and the addition that produces `elem` + // must not overflow or wrap around, so `elem >= base > 0`. + // + // TODO(#429): Once `NonNull::new_unchecked` documents that it + // preserves provenance, cite those docs. + let elem = unsafe { NonNull::new_unchecked(elem) }; + + // SAFETY: The safety invariants of `Ptr` (see definition) are + // satisfied: + // 1. `elem` is derived from a valid Rust allocation, because + // `self` is derived from a valid Rust allocation, by + // invariant on `Ptr` + // 2. `elem` has the same provenance as `self`, because it + // derived from `self` using a series of + // provenance-preserving operations + // 3. `elem` is entirely contained in the allocation of `self` + // (see above) + // 4. `elem` addresses a byte range whose length fits in an + // `isize` (see above) + // 5. `elem` addresses a byte range which does not wrap around + // the address space (see above) + // 6. `elem` is validly-aligned for `T`. `self`, which + // represents a `[T]` is validly aligned for `T`, and `elem` + // is an element within that `[T]` + // 7. The allocation of `elem` is guaranteed to live for at + // least `'a`, because `elem` is entirely contained in + // `self`, which lives for at least `'a` by invariant on + // `Ptr`. + // 8. `T: 'a`, because `elem` is an element within `[T]`, and + // `[T]: 'a` by invariant on `Ptr` + Ptr { ptr: elem, _lifetime: PhantomData } + }) + } } impl<'a, T: 'a + ?Sized> From<&'a T> for Ptr<'a, T> {