From b9c4311fea6b5a61fc444baaf68b2479aa414f7a Mon Sep 17 00:00:00 2001 From: Marc Date: Wed, 17 Oct 2018 15:48:25 +1300 Subject: [PATCH] Add OneConst and ZeroConst traits Conditional probing in build script Force enable with crate feature "associated_consts" Update README Add test const_identities --- Cargo.toml | 1 + README.md | 4 ++ build.rs | 10 ++++ src/identities.rs | 148 ++++++++++++++++++++++++++++++++++++---------- src/lib.rs | 2 + 5 files changed, 134 insertions(+), 31 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 672b4c2..146349b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -22,3 +22,4 @@ features = ["std"] default = ["std"] std = [] i128 = [] +associated_consts = [] diff --git a/README.md b/README.md index d2ea0d0..d0cbeca 100644 --- a/README.md +++ b/README.md @@ -42,6 +42,10 @@ Implementations for `i128` and `u128` are only available with Rust 1.26 and later. The build script automatically detects this, but you can make it mandatory by enabling the `i128` crate feature. +Similarly, additional traits making use of associated constants are available +with Rust 1.22 and later. Again, the build script automatically detects this, +but you can make it mandatory by enabling the `associated_consts` crate feature. + ## Releases Release notes are available in [RELEASES.md](RELEASES.md). diff --git a/build.rs b/build.rs index fd60866..3d399d1 100644 --- a/build.rs +++ b/build.rs @@ -8,6 +8,16 @@ fn main() { } else if env::var_os("CARGO_FEATURE_I128").is_some() { panic!("i128 support was not detected!"); } + + if probe("fn main() { + struct TestDrop(); impl Drop for TestDrop { fn drop(&mut self) {} } + trait TestConst { const TEST: Self; } + impl TestConst for TestDrop { const TEST: TestDrop = TestDrop(); } + }") { + println!("cargo:rustc-cfg=has_associated_consts"); + } else if env::var_os("CARGO_FEATURE_ASSOCIATED_CONSTS").is_some() { + panic!("associated constant support was not detected!"); + } } /// Test if a code snippet can be compiled diff --git a/src/identities.rs b/src/identities.rs index eadd018..45c8cc6 100644 --- a/src/identities.rs +++ b/src/identities.rs @@ -29,6 +29,18 @@ pub trait Zero: Sized + Add { fn is_zero(&self) -> bool; } +/// Supplimentary trait for [`Zero`](trait.Zero.html) types which can be +/// expressed as compile-time constants. +/// +/// This is implemented for all primitive types, and should be implemented +/// wherever possible. Implementors must ensure that `ZeroConst::ZERO` is +/// the same value produced by [`Zero::zero()`](trait.Zero.html#tymethod.zero). +#[cfg(has_associated_consts)] +pub trait ZeroConst: Zero { + /// Additive identity: see [`Zero::zero()`](trait.Zero.html#tymethod.zero). + const ZERO: Self; +} + macro_rules! zero_impl { ($t:ty, $v:expr) => { impl Zero for $t { @@ -44,24 +56,38 @@ macro_rules! zero_impl { }; } -zero_impl!(usize, 0); -zero_impl!(u8, 0); -zero_impl!(u16, 0); -zero_impl!(u32, 0); -zero_impl!(u64, 0); -#[cfg(has_i128)] -zero_impl!(u128, 0); +#[cfg(has_associated_consts)] +macro_rules! zero_const_impl { + ($t:ty, $v:expr) => { + zero_impl!($t, $v); + impl ZeroConst for $t { + const ZERO: $t = $v; + } + }; +} +#[cfg(not(has_associated_consts))] +macro_rules! zero_const_impl { + ($t:ty, $v:expr) => { zero_impl!($t, $v); }; +} -zero_impl!(isize, 0); -zero_impl!(i8, 0); -zero_impl!(i16, 0); -zero_impl!(i32, 0); -zero_impl!(i64, 0); +zero_const_impl!(usize, 0); +zero_const_impl!(u8, 0); +zero_const_impl!(u16, 0); +zero_const_impl!(u32, 0); +zero_const_impl!(u64, 0); #[cfg(has_i128)] -zero_impl!(i128, 0); +zero_const_impl!(u128, 0); -zero_impl!(f32, 0.0); -zero_impl!(f64, 0.0); +zero_const_impl!(isize, 0); +zero_const_impl!(i8, 0); +zero_const_impl!(i16, 0); +zero_const_impl!(i32, 0); +zero_const_impl!(i64, 0); +#[cfg(has_i128)] +zero_const_impl!(i128, 0); + +zero_const_impl!(f32, 0.0); +zero_const_impl!(f64, 0.0); impl Zero for Wrapping where @@ -80,6 +106,14 @@ where } } +#[cfg(has_associated_consts)] +impl ZeroConst for Wrapping +where + Wrapping: Zero, +{ + const ZERO: Self = Wrapping(T::ZERO); +} + /// Defines a multiplicative identity element for `Self`. /// /// # Laws @@ -118,6 +152,18 @@ pub trait One: Sized + Mul { } } +/// Supplimentary trait for [`One`](trait.One.html) types which can be +/// expressed as compile-time constants. +/// +/// This is implemented for all primitive types, and should be implemented +/// wherever possible. Implementors must ensure that `OneConst::ONE` is +/// the same value produced by [`One::one()`](trait.One.html#tymethod.one). +#[cfg(has_associated_consts)] +pub trait OneConst: One { + /// Multiplicative identity: see [`One::one`](trait.One.html#tymethod.one). + const ONE: Self; +} + macro_rules! one_impl { ($t:ty, $v:expr) => { impl One for $t { @@ -133,24 +179,39 @@ macro_rules! one_impl { }; } -one_impl!(usize, 1); -one_impl!(u8, 1); -one_impl!(u16, 1); -one_impl!(u32, 1); -one_impl!(u64, 1); -#[cfg(has_i128)] -one_impl!(u128, 1); +#[cfg(has_associated_consts)] +macro_rules! one_const_impl { + ($t:ty, $v:expr) => { + one_impl!($t, $v); + impl OneConst for $t { + const ONE: $t = $v; + } + }; +} +#[cfg(not(has_associated_consts))] +macro_rules! one_const_impl { + ($t:ty, $v:expr) => { one_impl!($t, $v); }; +} -one_impl!(isize, 1); -one_impl!(i8, 1); -one_impl!(i16, 1); -one_impl!(i32, 1); -one_impl!(i64, 1); -#[cfg(has_i128)] -one_impl!(i128, 1); -one_impl!(f32, 1.0); -one_impl!(f64, 1.0); +one_const_impl!(usize, 1); +one_const_impl!(u8, 1); +one_const_impl!(u16, 1); +one_const_impl!(u32, 1); +one_const_impl!(u64, 1); +#[cfg(has_i128)] +one_const_impl!(u128, 1); + +one_const_impl!(isize, 1); +one_const_impl!(i8, 1); +one_const_impl!(i16, 1); +one_const_impl!(i32, 1); +one_const_impl!(i64, 1); +#[cfg(has_i128)] +one_const_impl!(i128, 1); + +one_const_impl!(f32, 1.0); +one_const_impl!(f64, 1.0); impl One for Wrapping where @@ -165,6 +226,14 @@ where } } +#[cfg(has_associated_consts)] +impl OneConst for Wrapping +where + Wrapping: One, +{ + const ONE: Self = Wrapping(T::ONE); +} + // Some helper functions provided for backwards compatibility. /// Returns the additive identity, `0`. @@ -179,6 +248,23 @@ pub fn one() -> T { One::one() } +#[test] +#[cfg(has_associated_consts)] +fn const_identies() { + macro_rules! test_zero_one { + ($zero:expr, $one:expr; $($t:ty),+) => { + $( + assert_eq!(<$t as ZeroConst>::ZERO, $zero); + assert_eq!(<$t as ZeroConst>::ZERO, <$t as Zero>::zero()); + assert_eq!(<$t as OneConst>::ONE, $one); + assert_eq!(<$t as OneConst>::ONE, <$t as One>::one()); + )+ + } + } + test_zero_one!(0, 1; isize, i8, i16, i32, i64, usize, u8, u16, u32, u64); + test_zero_one!(0.0, 1.0; f32, f64); +} + #[test] fn wrapping_identities() { macro_rules! test_wrapping_identities { diff --git a/src/lib.rs b/src/lib.rs index 5a25bab..675d6f1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -32,6 +32,8 @@ pub use float::FloatConst; // pub use real::{FloatCore, Real}; // NOTE: Don't do this, it breaks `use num_traits::*;`. pub use cast::{cast, AsPrimitive, FromPrimitive, NumCast, ToPrimitive}; pub use identities::{one, zero, One, Zero}; +#[cfg(has_associated_consts)] +pub use identities::{OneConst, ZeroConst}; pub use int::PrimInt; pub use ops::checked::{ CheckedAdd, CheckedDiv, CheckedMul, CheckedNeg, CheckedRem, CheckedShl, CheckedShr, CheckedSub,