Skip to content

Commit

Permalink
Lower MSRV to 1.56
Browse files Browse the repository at this point in the history
Makes progress on #554
  • Loading branch information
joshlf committed Feb 8, 2024
1 parent 3974be4 commit 39da039
Show file tree
Hide file tree
Showing 55 changed files with 785 additions and 860 deletions.
11 changes: 8 additions & 3 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ jobs:
matrix:
# See `INTERNAL.md` for an explanation of these pinned toolchain
# versions.
toolchain: [ "msrv", "stable", "nightly", "zerocopy-aarch64-simd", "zerocopy-generic-bounds-in-const-fn" ]
toolchain: [ "msrv", "stable", "nightly", "zerocopy-generic-bounds-in-const-fn", "zerocopy-aarch64-simd", "zerocopy-panic-in-const", ]
target: [
"i686-unknown-linux-gnu",
"x86_64-unknown-linux-gnu",
Expand All @@ -59,10 +59,13 @@ jobs:
features: "--all-features"
- toolchain: "stable"
features: "--all-features"
- toolchain: "zerocopy-generic-bounds-in-const-fn"
features: "--all-features"
- toolchain: "zerocopy-aarch64-simd"
features: "--all-features"
- toolchain: "zerocopy-generic-bounds-in-const-fn"
- toolchain: "zerocopy-panic-in-const"
features: "--all-features"

# Exclude any combination for the zerocopy-derive crate which
# uses zerocopy features.
- crate: "zerocopy-derive"
Expand All @@ -75,10 +78,12 @@ jobs:
# other than "msrv", "stable", and "nightly". These other versions
# exist to exercise zerocopy behavior which differs by toolchain;
# zerocopy-derive doesn't behave different on these toolchains.
- crate: "zerocopy-derive"
toolchain: "zerocopy-generic-bounds-in-const-fn"
- crate: "zerocopy-derive"
toolchain: "zerocopy-aarch64-simd"
- crate: "zerocopy-derive"
toolchain: "zerocopy-generic-bounds-in-const-fn"
toolchain: "zerocopy-panic-in-const"

name: Build & Test (crate:${{ matrix.crate }}, toolchain:${{ matrix.toolchain }}, target:${{ matrix.target }}, features:${{ matrix.features }})

Expand Down
9 changes: 6 additions & 3 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ authors = ["Joshua Liebow-Feeser <[email protected]>"]
description = "Utilities for zero-copy parsing and serialization"
license = "BSD-2-Clause OR Apache-2.0 OR MIT"
repository = "https://github.com/google/zerocopy"
rust-version = "1.57.0"
rust-version = "1.56.0"

exclude = [".*"]

Expand All @@ -30,13 +30,16 @@ exclude = [".*"]
# as high as the specified version. In the emitted `--cfg`, dashes are replaced
# by underscores.

# From 1.61.0, Rust supports generic types with trait bounds in `const fn`.
zerocopy-generic-bounds-in-const-fn = "1.61.0"

# When the "simd" feature is enabled, include SIMD types from the
# `core::arch::aarch64` module, which was stabilized in 1.59.0. On earlier Rust
# versions, these types require the "simd-nightly" feature.
zerocopy-aarch64-simd = "1.59.0"

# From 1.61.0, Rust supports generic types with trait bounds in `const fn`.
zerocopy-generic-bounds-in-const-fn = "1.61.0"
# Permit panicking in `const fn`s.
zerocopy-panic-in-const = "1.57.0"

[package.metadata.ci]
# The versions of the stable and nightly compiler toolchains to use in CI.
Expand Down
48 changes: 26 additions & 22 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -363,13 +363,15 @@ pub struct DstLayout {
size_info: SizeInfo,
}

#[cfg_attr(any(kani, test), derive(Copy, Clone, Debug, PartialEq, Eq))]
#[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
enum SizeInfo<E = usize> {
Sized { _size: usize },
SliceDst(TrailingSliceLayout<E>),
}

#[cfg_attr(any(kani, test), derive(Copy, Clone, Debug, PartialEq, Eq))]
#[cfg_attr(any(kani, test), derive(Debug, PartialEq, Eq))]
#[derive(Copy, Clone)]
struct TrailingSliceLayout<E = usize> {
// The offset of the first byte of the trailing slice field. Note that this
// is NOT the same as the minimum size of the type. For example, consider
Expand Down Expand Up @@ -419,7 +421,7 @@ impl DstLayout {
/// The minimum possible alignment of a type.
const MIN_ALIGN: NonZeroUsize = match NonZeroUsize::new(1) {
Some(min_align) => min_align,
None => unreachable!(),
None => const_unreachable!(),
};

/// The maximum theoretic possible alignment of a type.
Expand All @@ -430,7 +432,7 @@ impl DstLayout {
const THEORETICAL_MAX_ALIGN: NonZeroUsize =
match NonZeroUsize::new(1 << (POINTER_WIDTH_BITS - 1)) {
Some(max_align) => max_align,
None => unreachable!(),
None => const_unreachable!(),
};

/// The current, documented max alignment of a type \[1\].
Expand All @@ -439,10 +441,10 @@ impl DstLayout {
///
/// The alignment value must be a power of two from 1 up to
/// 2<sup>29</sup>.
#[cfg(not(kani))]
#[cfg(all(not(kani), any(test, zerocopy_panic_in_const)))]
const CURRENT_MAX_ALIGN: NonZeroUsize = match NonZeroUsize::new(1 << 28) {
Some(max_align) => max_align,
None => unreachable!(),
None => const_unreachable!(),
};

/// Constructs a `DstLayout` for a zero-sized type with `repr_align`
Expand All @@ -464,7 +466,7 @@ impl DstLayout {
None => Self::MIN_ALIGN,
};

assert!(align.get().is_power_of_two());
const_assert!(align.get().is_power_of_two());

DstLayout { align, size_info: SizeInfo::Sized { _size: 0 } }
}
Expand All @@ -483,7 +485,7 @@ impl DstLayout {
DstLayout {
align: match NonZeroUsize::new(mem::align_of::<T>()) {
Some(align) => align,
None => unreachable!(),
None => const_unreachable!(),
},
size_info: SizeInfo::Sized { _size: mem::size_of::<T>() },
}
Expand All @@ -506,7 +508,7 @@ impl DstLayout {
DstLayout {
align: match NonZeroUsize::new(mem::align_of::<T>()) {
Some(align) => align,
None => unreachable!(),
None => const_unreachable!(),
},
size_info: SizeInfo::SliceDst(TrailingSliceLayout {
_offset: 0,
Expand Down Expand Up @@ -552,12 +554,12 @@ impl DstLayout {
None => Self::THEORETICAL_MAX_ALIGN,
};

assert!(max_align.get().is_power_of_two());
const_assert!(max_align.get().is_power_of_two());

// We use Kani to prove that this method is robust to future increases
// in Rust's maximum allowed alignment. However, if such a change ever
// actually occurs, we'd like to be notified via assertion failures.
#[cfg(not(kani))]
#[cfg(all(not(kani), zerocopy_panic_in_const))]
{
debug_assert!(self.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get());
debug_assert!(field.align.get() <= DstLayout::CURRENT_MAX_ALIGN.get());
Expand All @@ -583,7 +585,7 @@ impl DstLayout {
let size_info = match self.size_info {
// If the layout is already a DST, we panic; DSTs cannot be extended
// with additional fields.
SizeInfo::SliceDst(..) => panic!("Cannot extend a DST with additional fields."),
SizeInfo::SliceDst(..) => const_panic!("Cannot extend a DST with additional fields."),

SizeInfo::Sized { _size: preceding_size } => {
// Compute the minimum amount of inter-field padding needed to
Expand All @@ -604,7 +606,7 @@ impl DstLayout {
// exceeding `isize::MAX`).
let offset = match preceding_size.checked_add(padding) {
Some(offset) => offset,
None => panic!("Adding padding to `self`'s size overflows `usize`."),
None => const_panic!("Adding padding to `self`'s size overflows `usize`."),
};

match field.size_info {
Expand All @@ -622,7 +624,7 @@ impl DstLayout {
// `usize::MAX`).
let size = match offset.checked_add(field_size) {
Some(size) => size,
None => panic!("`field` cannot be appended without the total size overflowing `usize`"),
None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"),
};
SizeInfo::Sized { _size: size }
}
Expand All @@ -644,7 +646,7 @@ impl DstLayout {
// `usize::MAX`).
let offset = match offset.checked_add(trailing_offset) {
Some(offset) => offset,
None => panic!("`field` cannot be appended without the total size overflowing `usize`"),
None => const_panic!("`field` cannot be appended without the total size overflowing `usize`"),
};
SizeInfo::SliceDst(TrailingSliceLayout { _offset: offset, _elem_size })
}
Expand Down Expand Up @@ -691,7 +693,7 @@ impl DstLayout {
let padding = padding_needed_for(unpadded_size, self.align);
let size = match unpadded_size.checked_add(padding) {
Some(size) => size,
None => panic!("Adding padding caused size to overflow `usize`."),
None => const_panic!("Adding padding caused size to overflow `usize`."),
};
SizeInfo::Sized { _size: size }
}
Expand Down Expand Up @@ -776,9 +778,11 @@ impl DstLayout {
bytes_len: usize,
cast_type: _CastType,
) -> Option<(usize, usize)> {
// `debug_assert!`, but with `#[allow(clippy::arithmetic_side_effects)]`.
// `debug_assert!`, but with `#[allow(clippy::arithmetic_side_effects)]`
// and `#[cfg(zerocopy_panic_in_const)]`.
macro_rules! __debug_assert {
($e:expr $(, $msg:expr)?) => {
#[cfg(zerocopy_panic_in_const)]
debug_assert!({
#[allow(clippy::arithmetic_side_effects)]
let e = $e;
Expand All @@ -798,7 +802,7 @@ impl DstLayout {
// https://blog.rust-lang.org/2022/11/03/Rust-1.65.0.html#let-else-statements
let size_info = match self.size_info.try_to_nonzero_elem_size() {
Some(size_info) => size_info,
None => panic!("attempted to cast to slice type with zero-sized element"),
None => const_panic!("attempted to cast to slice type with zero-sized element"),
};

// Precondition
Expand Down Expand Up @@ -3951,7 +3955,7 @@ macro_rules! transmute_ref {

// `t` is inferred to have type `T` because it's assigned to `e` (of
// type `&T`) as `&t`.
let mut t = unreachable!();
let mut t = loop {};
e = &t;

// `u` is inferred to have type `U` because it's used as `&u` as the
Expand Down Expand Up @@ -5324,7 +5328,7 @@ impl<'a> sealed::ByteSliceSealed for cell::Ref<'a, [u8]> {}
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for cell::Ref<'a, [u8]> {
const INTO_REF_INTO_MUT_ARE_SOUND: bool = if !cfg!(doc) {
panic!("Ref::into_ref and Ref::into_mut are unsound when used with core::cell::Ref; see https://github.com/google/zerocopy/issues/716")
const_panic!("Ref::into_ref and Ref::into_mut are unsound when used with core::cell::Ref; see https://github.com/google/zerocopy/issues/716")
} else {
// When compiling documentation, allow the evaluation of this constant
// to succeed. This doesn't represent a soundness hole - it just delays
Expand All @@ -5344,7 +5348,7 @@ impl<'a> sealed::ByteSliceSealed for RefMut<'a, [u8]> {}
#[allow(clippy::undocumented_unsafe_blocks)]
unsafe impl<'a> ByteSlice for RefMut<'a, [u8]> {
const INTO_REF_INTO_MUT_ARE_SOUND: bool = if !cfg!(doc) {
panic!("Ref::into_ref and Ref::into_mut are unsound when used with core::cell::RefMut; see https://github.com/google/zerocopy/issues/716")
const_panic!("Ref::into_ref and Ref::into_mut are unsound when used with core::cell::RefMut; see https://github.com/google/zerocopy/issues/716")
} else {
// When compiling documentation, allow the evaluation of this constant
// to succeed. This doesn't represent a soundness hole - it just delays
Expand Down Expand Up @@ -5810,7 +5814,7 @@ mod tests {
// attempt to expose UB.
#[test]
#[cfg_attr(miri, ignore)]
fn testvalidate_cast_and_convert_metadata() {
fn test_validate_cast_and_convert_metadata() {
impl From<usize> for SizeInfo {
fn from(_size: usize) -> SizeInfo {
SizeInfo::Sized { _size }
Expand Down
52 changes: 52 additions & 0 deletions src/macros.rs
Original file line number Diff line number Diff line change
Expand Up @@ -433,3 +433,55 @@ macro_rules! maybe_const_trait_bounded_fn {
$(#[$attr])* $vis fn $name($($args $(: $arg_tys)?),*) $(-> $ret_ty)? $body
};
}

/// Either panic (if the current Rust toolchain supports panicking in `const
/// fn`) or evaluate a constant that will cause an array indexing error whose
/// error message will include the format string.
///
/// The type that this expression evaluates to must be `Copy`, or else the
/// non-panicking desugaring will fail to compile.
macro_rules! const_panic {
($fmt:literal) => {{
#[cfg(zerocopy_panic_in_const)]
panic!($fmt);
#[cfg(not(zerocopy_panic_in_const))]
const_panic!(@non_panic $fmt)
}};
(@non_panic $fmt:expr) => {{
// This will type check to whatever type is expected based on the call
// site.
let panic: [_; 0] = [];
// This will always fail (since we're indexing into an array of size 0.
#[allow(unconditional_panic)]
panic[0]
}}
}

/// Either assert (if the current Rust toolchain supports panicking in `const
/// fn`) or evaluate the expression and, if it evaluates to `false`, call
/// `const_panic!`.
macro_rules! const_assert {
($e:expr) => {{
#[cfg(zerocopy_panic_in_const)]
assert!($e);
#[cfg(not(zerocopy_panic_in_const))]
{
let e = $e;
if !e {
let _: () = const_panic!(@non_panic concat!("assertion failed: ", stringify!($e)));
}
}
}}
}

/// Either invoke `unreachable!()` or `loop {}` depending on whether the Rust
/// toolchain supports panicking in `const fn`.
macro_rules! const_unreachable {
() => {{
#[cfg(zerocopy_panic_in_const)]
unreachable!();

#[cfg(not(zerocopy_panic_in_const))]
loop {}
}};
}
8 changes: 8 additions & 0 deletions src/util.rs
Original file line number Diff line number Diff line change
Expand Up @@ -129,6 +129,7 @@ pub(crate) const fn round_down_to_next_multiple_of_alignment(
align: NonZeroUsize,
) -> usize {
let align = align.get();
#[cfg(zerocopy_panic_in_const)]
debug_assert!(align.is_power_of_two());

// Subtraction can't underflow because `align.get() >= 1`.
Expand Down Expand Up @@ -275,6 +276,13 @@ mod tests {
}
}
}

#[rustversion::since(1.57.0)]
#[test]
#[should_panic]
fn test_round_down_to_next_multiple_of_alignment_panic_in_const() {
round_down_to_next_multiple_of_alignment(0, NonZeroUsize::new(3).unwrap());
}
}

#[cfg(kani)]
Expand Down
2 changes: 1 addition & 1 deletion src/wrappers.rs
Original file line number Diff line number Diff line change
Expand Up @@ -477,7 +477,7 @@ mod tests {
let au64 = unsafe { x.t.deref_unchecked() };
match au64 {
AU64(123) => {}
_ => unreachable!(),
_ => const_unreachable!(),
}
};
}
Expand Down
21 changes: 4 additions & 17 deletions tests/ui-stable/include_value_not_from_bytes.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -2,24 +2,11 @@ error[E0277]: the trait bound `NotZerocopy<u32>: FromBytes` is not satisfied
--> tests/ui-stable/include_value_not_from_bytes.rs:13:42
|
13 | const NOT_FROM_BYTES: NotZerocopy<u32> = include_value!("../../testdata/include_value/data");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
| |
| the trait `FromBytes` is not implemented for `NotZerocopy<u32>`
| required by a bound introduced by this call
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ the trait `FromBytes` is not implemented for `NotZerocopy<u32>`
|
= help: the following other types implement trait `FromBytes`:
isize
i8
i16
i32
i64
i128
usize
u8
and $N others
note: required by a bound in `AssertIsFromBytes`
note: required by `AssertIsFromBytes`
--> tests/ui-stable/include_value_not_from_bytes.rs:13:42
|
13 | const NOT_FROM_BYTES: NotZerocopy<u32> = include_value!("../../testdata/include_value/data");
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in `AssertIsFromBytes`
= note: this error originates in the macro `$crate::transmute` which comes from the expansion of the macro `include_value` (in Nightly builds, run with -Z macro-backtrace for more info)
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
= note: this error originates in the macro `$crate::transmute` (in Nightly builds, run with -Z macro-backtrace for more info)
2 changes: 1 addition & 1 deletion tests/ui-stable/include_value_wrong_size.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ error[E0512]: cannot transmute between types of different sizes, or dependently-
|
= note: source type: `[u8; 4]` (32 bits)
= note: target type: `u64` (64 bits)
= note: this error originates in the macro `$crate::transmute` which comes from the expansion of the macro `include_value` (in Nightly builds, run with -Z macro-backtrace for more info)
= note: this error originates in the macro `$crate::transmute` (in Nightly builds, run with -Z macro-backtrace for more info)
Loading

0 comments on commit 39da039

Please sign in to comment.