Skip to content

Commit

Permalink
Reduce MSRV to 1.61.0 (#234)
Browse files Browse the repository at this point in the history
It turned out that there were only a few lines of code that required
our MSRV to be as high as it was before this commit (1.65.0), and those
lines were easy to modify to be compatible with this new MSRV.

Note that this requires introducing defensive code to
`FromZeroes::new_box_slice_zeroed` in order to sidestep a bug in
`Layout::from_size_align` that was present through 1.64.0.
  • Loading branch information
joshlf authored Aug 8, 2023
1 parent 8effca5 commit 9f35f36
Show file tree
Hide file tree
Showing 12 changed files with 81 additions and 208 deletions.
2 changes: 1 addition & 1 deletion Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ authors = ["Joshua Liebow-Feeser <joshlf@google.com>"]
description = "Utilities for zero-copy parsing and serialization"
license = "BSD-2-Clause"
repository = "https://github.com/google/zerocopy"
rust-version = "1.65.0"
rust-version = "1.61.0"

exclude = [".*"]

Expand Down
92 changes: 41 additions & 51 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -334,15 +334,23 @@ pub unsafe trait FromZeroes {
where
Self: Sized,
{
let size = mem::size_of::<Self>()
.checked_mul(len)
.expect("mem::size_of::<Self>() * len overflows `usize`");
let align = mem::align_of::<Self>();
// On stable Rust versions <= 1.64.0, `Layout::from_size_align` has a
// bug in which sufficiently-large allocations (those which, when
// rounded up to the alignment, overflow `isize`) are not rejected,
// which can cause undefined behavior. See #64 for details.
//
// TODO(#67): Once our MSRV is > 1.64.0, remove this assertion.
#[allow(clippy::as_conversions)]
let max_alloc = (isize::MAX as usize).saturating_sub(align);
assert!(size <= max_alloc);
// TODO(#2): Use `Layout::repeat` when `alloc_layout_extra` is
// stabilized.
let layout = Layout::from_size_align(
mem::size_of::<Self>()
.checked_mul(len)
.expect("mem::size_of::<Self>() * len overflows `usize`"),
mem::align_of::<Self>(),
)
.expect("total allocation size overflows `isize`");
let layout =
Layout::from_size_align(size, align).expect("total allocation size overflows `isize`");

// TODO(#61): Add a "SAFETY" comment and remove this `allow`.
#[allow(clippy::undocumented_unsafe_blocks)]
Expand Down Expand Up @@ -1265,12 +1273,16 @@ impl<T> Unalign<T> {
/// If `self` does not satisfy `mem::align_of::<T>()`, then
/// `self.deref_unchecked()` may cause undefined behavior.
pub const unsafe fn deref_unchecked(&self) -> &T {
// SAFETY: `self.get_ptr()` returns a raw pointer to a valid `T` at the
// same memory location as `self`. It has no alignment guarantee, but
// the caller has promised that `self` is properly aligned, so we know
// that the pointer itself is aligned, and thus that it is sound to
// create a reference to a `T` at this memory location.
unsafe { &*self.get_ptr() }
// SAFETY: `Unalign<T>` is `repr(transparent)`, so there is a valid `T`
// at the same memory location as `self`. It has no alignment guarantee,
// but the caller has promised that `self` is properly aligned, so we
// know that it is sound to create a reference to `T` at this memory
// location.
//
// We use `mem::transmute` instead of `&*self.get_ptr()` because
// dereferencing pointers is not stable in `const` on our current MSRV
// (1.56 as of this writing).
unsafe { mem::transmute(self) }
}

/// Returns a mutable reference to the wrapped `T` without checking
Expand Down Expand Up @@ -1518,6 +1530,10 @@ macro_rules! transmute {
// were to use `core::mem::transmute`, this macro would not work in
// `std` contexts in which `core` was not manually imported. This is
// not a problem for 2018 edition crates.
//
// Some older versions of Clippy have a bug in which they don't
// recognize the preceding safety comment.
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe { $crate::__real_transmute(e) }
}
}}
Expand Down Expand Up @@ -2782,17 +2798,12 @@ mod alloc_support {

#[cfg(test)]
mod tests {
use core::convert::TryFrom as _;

use super::*;

#[test]
fn test_extend_vec_zeroed() {
// Test extending when there is an existing allocation.
let mut v: Vec<u64> = Vec::with_capacity(3);
v.push(100);
v.push(200);
v.push(300);
let mut v = vec![100u64, 200, 300];
extend_vec_zeroed(&mut v, 3);
assert_eq!(v.len(), 6);
assert_eq!(&*v, &[100, 200, 300, 0, 0, 0]);
Expand All @@ -2809,10 +2820,7 @@ mod alloc_support {
#[test]
fn test_extend_vec_zeroed_zst() {
// Test extending when there is an existing (fake) allocation.
let mut v: Vec<()> = Vec::with_capacity(3);
v.push(());
v.push(());
v.push(());
let mut v = vec![(), (), ()];
extend_vec_zeroed(&mut v, 3);
assert_eq!(v.len(), 6);
assert_eq!(&*v, &[(), (), (), (), (), ()]);
Expand All @@ -2835,30 +2843,21 @@ mod alloc_support {
drop(v);

// Insert at start.
let mut v: Vec<u64> = Vec::with_capacity(3);
v.push(100);
v.push(200);
v.push(300);
let mut v = vec![100u64, 200, 300];
insert_vec_zeroed(&mut v, 0, 2);
assert_eq!(v.len(), 5);
assert_eq!(&*v, &[0, 0, 100, 200, 300]);
drop(v);

// Insert at middle.
let mut v: Vec<u64> = Vec::with_capacity(3);
v.push(100);
v.push(200);
v.push(300);
let mut v = vec![100u64, 200, 300];
insert_vec_zeroed(&mut v, 1, 1);
assert_eq!(v.len(), 4);
assert_eq!(&*v, &[100, 0, 200, 300]);
drop(v);

// Insert at end.
let mut v: Vec<u64> = Vec::with_capacity(3);
v.push(100);
v.push(200);
v.push(300);
let mut v = vec![100u64, 200, 300];
insert_vec_zeroed(&mut v, 3, 1);
assert_eq!(v.len(), 4);
assert_eq!(&*v, &[100, 200, 300, 0]);
Expand All @@ -2875,30 +2874,21 @@ mod alloc_support {
drop(v);

// Insert at start.
let mut v: Vec<()> = Vec::with_capacity(3);
v.push(());
v.push(());
v.push(());
let mut v = vec![(), (), ()];
insert_vec_zeroed(&mut v, 0, 2);
assert_eq!(v.len(), 5);
assert_eq!(&*v, &[(), (), (), (), ()]);
drop(v);

// Insert at middle.
let mut v: Vec<()> = Vec::with_capacity(3);
v.push(());
v.push(());
v.push(());
let mut v = vec![(), (), ()];
insert_vec_zeroed(&mut v, 1, 1);
assert_eq!(v.len(), 4);
assert_eq!(&*v, &[(), (), (), ()]);
drop(v);

// Insert at end.
let mut v: Vec<()> = Vec::with_capacity(3);
v.push(());
v.push(());
v.push(());
let mut v = vec![(), (), ()];
insert_vec_zeroed(&mut v, 3, 1);
assert_eq!(v.len(), 4);
assert_eq!(&*v, &[(), (), (), ()]);
Expand Down Expand Up @@ -2967,7 +2957,7 @@ mod alloc_support {
}

#[test]
#[should_panic(expected = "total allocation size overflows `isize`: LayoutError")]
#[should_panic(expected = "assertion failed: size <= max_alloc")]
fn test_new_box_slice_zeroed_panics_isize_overflow() {
let max = usize::try_from(isize::MAX).unwrap();
let _ = u16::new_box_slice_zeroed((max / mem::size_of::<u16>()) + 1);
Expand Down Expand Up @@ -3758,7 +3748,7 @@ mod tests {
/// has had its bits flipped (by applying `^= 0xFF`).
///
/// `N` is the size of `t` in bytes.
fn test<const N: usize, T: FromBytes + AsBytes + Debug + Eq + ?Sized>(
fn test<T: FromBytes + AsBytes + Debug + Eq + ?Sized, const N: usize>(
t: &mut T,
bytes: &[u8],
post_mutation: &T,
Expand Down Expand Up @@ -3830,12 +3820,12 @@ mod tests {
};
let post_mutation_expected_a =
if cfg!(target_endian = "little") { 0x00_00_00_FE } else { 0xFF_00_00_01 };
test::<12, _>(
test::<_, 12>(
&mut Foo { a: 1, b: Wrapping(2), c: None },
expected_bytes.as_bytes(),
&Foo { a: post_mutation_expected_a, b: Wrapping(2), c: None },
);
test::<3, _>(
test::<_, 3>(
Unsized::from_mut_slice(&mut [1, 2, 3]),
&[1, 2, 3],
Unsized::from_mut_slice(&mut [0xFE, 2, 3]),
Expand Down
6 changes: 3 additions & 3 deletions tests/trybuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
// - `tests/ui-msrv` - Contains symlinks to the `.rs` files in
// `tests/ui-nightly`, and contains `.err` and `.out` files for MSRV

#[rustversion::any(nightly)]
#[rustversion::nightly]
const SOURCE_FILES_GLOB: &str = "tests/ui-nightly/*.rs";
#[rustversion::all(stable, not(stable(1.65.0)))]
#[rustversion::stable(1.69.0)]
const SOURCE_FILES_GLOB: &str = "tests/ui-stable/*.rs";
#[rustversion::stable(1.65.0)]
#[rustversion::stable(1.61.0)]
const SOURCE_FILES_GLOB: &str = "tests/ui-msrv/*.rs";

#[test]
Expand Down
19 changes: 6 additions & 13 deletions tests/ui-msrv/transmute-illegal.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,13 @@ error[E0277]: the trait bound `*const usize: AsBytes` is not satisfied
--> tests/ui-msrv/transmute-illegal.rs:10:30
|
10 | const POINTER_VALUE: usize = zerocopy::transmute!(&0usize as *const usize);
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| the trait `AsBytes` is not implemented for `*const usize`
| required by a bound introduced by this call
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `AsBytes` is not implemented for `*const usize`
|
= help: the following other types implement trait `AsBytes`:
f32
f64
i128
i16
i32
i64
i8
isize
= help: the following implementations were found:
<usize as AsBytes>
<f32 as AsBytes>
<f64 as AsBytes>
<i128 as AsBytes>
and $N others
note: required by a bound in `POINTER_VALUE::transmute`
--> tests/ui-msrv/transmute-illegal.rs:10:30
Expand Down
2 changes: 1 addition & 1 deletion zerocopy-derive/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ authors = ["Joshua Liebow-Feeser <joshlf@google.com>"]
description = "Custom derive for traits from the zerocopy crate"
license = "BSD-2-Clause"
repository = "https://github.com/google/zerocopy"
rust-version = "1.65.0"
rust-version = "1.61.0"

exclude = [".*"]

Expand Down
4 changes: 2 additions & 2 deletions zerocopy-derive/src/repr.rs
Original file line number Diff line number Diff line change
Expand Up @@ -191,7 +191,7 @@ impl Repr {
let ident = path
.get_ident()
.ok_or_else(|| Error::new_spanned(meta, "unrecognized representation hint"))?;
match format!("{ident}").as_str() {
match format!("{}", ident).as_str() {
"u8" => return Ok(Repr::U8),
"u16" => return Ok(Repr::U16),
"u32" => return Ok(Repr::U32),
Expand Down Expand Up @@ -221,7 +221,7 @@ impl Repr {
impl Display for Repr {
fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), fmt::Error> {
if let Repr::Align(n) = self {
return write!(f, "repr(align({n}))");
return write!(f, "repr(align({}))", n);
}
write!(
f,
Expand Down
6 changes: 3 additions & 3 deletions zerocopy-derive/tests/trybuild.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,11 @@
// - `tests/ui-msrv` - Contains symlinks to the `.rs` files in
// `tests/ui-nightly`, and contains `.err` and `.out` files for MSRV

#[rustversion::any(nightly)]
#[rustversion::nightly]
const SOURCE_FILES_GLOB: &str = "tests/ui-nightly/*.rs";
#[rustversion::all(stable, not(stable(1.65.0)))]
#[rustversion::stable(1.69.0)]
const SOURCE_FILES_GLOB: &str = "tests/ui-stable/*.rs";
#[rustversion::stable(1.65.0)]
#[rustversion::stable(1.61.0)]
const SOURCE_FILES_GLOB: &str = "tests/ui-msrv/*.rs";

#[test]
Expand Down
Loading

0 comments on commit 9f35f36

Please sign in to comment.