From 84a8f32f18ab45952dc18423b3c32e99ae481c99 Mon Sep 17 00:00:00 2001 From: Joshua Liebow-Feeser Date: Thu, 8 Feb 2024 08:58:59 -0800 Subject: [PATCH] [byteorder] Make some methods const For methods which have generic types with trait bounds (other than `Sized`), conditionally compile as `const` only on Rust 1.61.0 or later. --- .github/workflows/ci.yml | 6 ++++- Cargo.toml | 3 +++ build.rs | 3 ++- src/byteorder.rs | 55 ++++++++++++++++++++++++++-------------- src/macros.rs | 16 ++++++++++++ 5 files changed, 62 insertions(+), 21 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 721b9cc45b..8734a43f60 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,7 +38,7 @@ jobs: matrix: # See `INTERNAL.md` for an explanation of these pinned toolchain # versions. - toolchain: [ "msrv", "stable", "nightly", "zerocopy-aarch64-simd" ] + toolchain: [ "msrv", "stable", "nightly", "zerocopy-aarch64-simd", "zerocopy-generic-bounds-in-const-fn" ] target: [ "i686-unknown-linux-gnu", "x86_64-unknown-linux-gnu", @@ -61,6 +61,8 @@ jobs: features: "--all-features" - toolchain: "zerocopy-aarch64-simd" features: "--all-features" + - toolchain: "zerocopy-generic-bounds-in-const-fn" + features: "--all-features" # Exclude any combination for the zerocopy-derive crate which # uses zerocopy features. - crate: "zerocopy-derive" @@ -75,6 +77,8 @@ jobs: # zerocopy-derive doesn't behave different on these toolchains. - crate: "zerocopy-derive" toolchain: "zerocopy-aarch64-simd" + - crate: "zerocopy-derive" + toolchain: "zerocopy-generic-bounds-in-const-fn" name: Build & Test (crate:${{ matrix.crate }}, toolchain:${{ matrix.toolchain }}, target:${{ matrix.target }}, features:${{ matrix.features }}) diff --git a/Cargo.toml b/Cargo.toml index aa2392fdee..db69c2372e 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -35,6 +35,9 @@ exclude = [".*"] # 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" + [package.metadata.ci] # The versions of the stable and nightly compiler toolchains to use in CI. pinned-stable = "1.75.0" diff --git a/build.rs b/build.rs index ceb61bee58..3842c3eddb 100644 --- a/build.rs +++ b/build.rs @@ -73,13 +73,14 @@ fn main() { } } -#[derive(Ord, PartialEq, PartialOrd, Eq)] +#[derive(Debug, Ord, PartialEq, PartialOrd, Eq)] struct Version { major: usize, minor: usize, patch: usize, } +#[derive(Debug)] struct VersionCfg { version: Version, cfg_name: String, diff --git a/src/byteorder.rs b/src/byteorder.rs index cf0517c23d..03dfe028af 100644 --- a/src/byteorder.rs +++ b/src/byteorder.rs @@ -401,7 +401,7 @@ example of how it can be used for parsing UDP packets. /// Constructs a new value from bytes which are already in `O` byte /// order. #[inline(always)] - pub fn from_bytes(bytes: [u8; $bytes]) -> $name { + pub const fn from_bytes(bytes: [u8; $bytes]) -> $name { $name(bytes, PhantomData) } @@ -409,32 +409,37 @@ example of how it can be used for parsing UDP packets. /// /// The returned bytes will be in `O` byte order. #[inline(always)] - pub fn to_bytes(self) -> [u8; $bytes] { + pub const fn to_bytes(self) -> [u8; $bytes] { self.0 } } impl $name { - /// Constructs a new value, possibly performing an endianness swap - /// to guarantee that the returned value has endianness `O`. - #[inline(always)] - pub fn new(n: $native) -> $name { - let bytes = match O::ORDER { - Order::BigEndian => $to_be_fn(n), - Order::LittleEndian => $to_le_fn(n), - }; + maybe_const_trait_bounded_fn! { + /// Constructs a new value, possibly performing an endianness + /// swap to guarantee that the returned value has endianness + /// `O`. + #[inline(always)] + pub const fn new(n: $native) -> $name { + let bytes = match O::ORDER { + Order::BigEndian => $to_be_fn(n), + Order::LittleEndian => $to_le_fn(n), + }; - $name(bytes, PhantomData) + $name(bytes, PhantomData) + } } - /// Returns the value as a primitive type, possibly performing an - /// endianness swap to guarantee that the return value has the - /// endianness of the native platform. - #[inline(always)] - pub fn get(self) -> $native { - match O::ORDER { - Order::BigEndian => $from_be_fn(self.0), - Order::LittleEndian => $from_le_fn(self.0), + maybe_const_trait_bounded_fn! { + /// Returns the value as a primitive type, possibly performing + /// an endianness swap to guarantee that the return value has + /// the endianness of the native platform. + #[inline(always)] + pub const fn get(self) -> $native { + match O::ORDER { + Order::BigEndian => $from_be_fn(self.0), + Order::LittleEndian => $from_le_fn(self.0), + } } } @@ -1123,6 +1128,18 @@ mod tests { 1024 }; + #[test] + fn test_const_methods() { + use big_endian::*; + + #[rustversion::since(1.61.0)] + const _U: U16 = U16::new(0); + #[rustversion::since(1.61.0)] + const _NATIVE: u16 = _U.get(); + const _FROM_BYTES: U16 = U16::from_bytes([0, 1]); + const _BYTES: [u8; 2] = _FROM_BYTES.to_bytes(); + } + #[cfg_attr(test, test)] #[cfg_attr(kani, kani::proof)] fn test_zero() { diff --git a/src/macros.rs b/src/macros.rs index e14920534a..fb8f05f4fc 100644 --- a/src/macros.rs +++ b/src/macros.rs @@ -416,3 +416,19 @@ macro_rules! assert_unaligned { $(assert_unaligned!($ty);)* }; } + +/// Emits a function definition as either `const fn` or `fn` depending on +/// whether the current toolchain version supports `const fn` with generic trait +/// bounds. +macro_rules! maybe_const_trait_bounded_fn { + // This case handles both `self` methods (where `self` is by value) and + // non-method functions. Each `$args` may optionally be followed by `: + // $arg_tys:ty`, which can be omitted for `self`. + ($(#[$attr:meta])* $vis:vis const fn $name:ident($($args:ident $(: $arg_tys:ty)?),* $(,)?) $(-> $ret_ty:ty)? $body:block) => { + #[cfg(zerocopy_generic_bounds_in_const_fn)] + $(#[$attr])* $vis const fn $name($($args $(: $arg_tys)?),*) $(-> $ret_ty)? $body + + #[cfg(not(zerocopy_generic_bounds_in_const_fn))] + $(#[$attr])* $vis fn $name($($args $(: $arg_tys)?),*) $(-> $ret_ty)? $body + }; +}