about summary refs log tree commit diff
diff options
context:
space:
mode:
authorTrevor Gross <tmgross@umich.edu>2024-10-31 23:53:03 -0500
committerTrevor Gross <t.gross35@gmail.com>2024-11-01 00:06:04 -0500
commitdbf8a4ebe5106a28c7c95dc87b38ce716d6950c4 (patch)
tree87a12f30cdc825e7bad41a2994d5a4484ef5b10b
parent4b12a8404f3397c93d59711a3499a8e86213a015 (diff)
downloadrust-dbf8a4ebe5106a28c7c95dc87b38ce716d6950c4.tar.gz
rust-dbf8a4ebe5106a28c7c95dc87b38ce716d6950c4.zip
Replace `libm_test::{Float, Int}` with `libm::{Float, Int}`
This involves moving some things from full generic implementations (e.g.
`impl<F: Float> SomeTrait for F { /* ... */ }` to generic functions and
macros to implement traits that call them, due to orphan rule violations
after `Float` became a not-in-crate trait.

`Hex` was moved to `test_traits` so we can eliminate `num_traits`.
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/Cargo.toml2
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/src/lib.rs5
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/src/num_traits.rs214
-rw-r--r--library/compiler-builtins/libm/crates/libm-test/src/test_traits.rs261
4 files changed, 199 insertions, 283 deletions
diff --git a/library/compiler-builtins/libm/crates/libm-test/Cargo.toml b/library/compiler-builtins/libm/crates/libm-test/Cargo.toml
index fedf745ed7f..3587b44e6a4 100644
--- a/library/compiler-builtins/libm/crates/libm-test/Cargo.toml
+++ b/library/compiler-builtins/libm/crates/libm-test/Cargo.toml
@@ -24,7 +24,7 @@ short-benchmarks = []
 [dependencies]
 anyhow = "1.0.90"
 az = { version = "1.2.1", optional = true }
-libm = { path = "../.." }
+libm = { path = "../..", features = ["unstable-test-support"] }
 libm-macros = { path = "../libm-macros" }
 musl-math-sys = { path = "../musl-math-sys", optional = true }
 paste = "1.0.15"
diff --git a/library/compiler-builtins/libm/crates/libm-test/src/lib.rs b/library/compiler-builtins/libm/crates/libm-test/src/lib.rs
index 6c7a3f5ece2..56a872779d6 100644
--- a/library/compiler-builtins/libm/crates/libm-test/src/lib.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/src/lib.rs
@@ -1,13 +1,12 @@
 pub mod gen;
 #[cfg(feature = "test-multiprecision")]
 pub mod mpfloat;
-mod num_traits;
 mod precision;
 mod test_traits;
 
-pub use num_traits::{Float, Hex, Int};
+pub use libm::support::{Float, Int};
 pub use precision::{MaybeOverride, SpecialCase, multiprec_allowed_ulp, musl_allowed_ulp};
-pub use test_traits::{CheckBasis, CheckCtx, CheckOutput, GenerateInput, TupleCall};
+pub use test_traits::{CheckBasis, CheckCtx, CheckOutput, GenerateInput, Hex, TupleCall};
 
 /// Result type for tests is usually from `anyhow`. Most times there is no success value to
 /// propagate.
diff --git a/library/compiler-builtins/libm/crates/libm-test/src/num_traits.rs b/library/compiler-builtins/libm/crates/libm-test/src/num_traits.rs
deleted file mode 100644
index e16f4e4dca4..00000000000
--- a/library/compiler-builtins/libm/crates/libm-test/src/num_traits.rs
+++ /dev/null
@@ -1,214 +0,0 @@
-use std::fmt;
-
-use crate::{MaybeOverride, SpecialCase, TestResult};
-
-/// Common types and methods for floating point numbers.
-pub trait Float: Copy + fmt::Display + fmt::Debug + PartialEq<Self> {
-    type Int: Int<OtherSign = Self::SignedInt, Unsigned = Self::Int>;
-    type SignedInt: Int + Int<OtherSign = Self::Int, Unsigned = Self::Int>;
-
-    const ZERO: Self;
-    const ONE: Self;
-
-    /// The bitwidth of the float type
-    const BITS: u32;
-
-    /// The bitwidth of the significand
-    const SIGNIFICAND_BITS: u32;
-
-    /// The bitwidth of the exponent
-    const EXPONENT_BITS: u32 = Self::BITS - Self::SIGNIFICAND_BITS - 1;
-
-    fn is_nan(self) -> bool;
-    fn is_infinite(self) -> bool;
-    fn to_bits(self) -> Self::Int;
-    fn from_bits(bits: Self::Int) -> Self;
-    fn signum(self) -> Self;
-}
-
-macro_rules! impl_float {
-    ($($fty:ty, $ui:ty, $si:ty, $significand_bits:expr;)+) => {
-        $(
-            impl Float for $fty {
-                type Int = $ui;
-                type SignedInt = $si;
-
-                const ZERO: Self = 0.0;
-                const ONE: Self = 1.0;
-
-                const BITS: u32 = <$ui>::BITS;
-                const SIGNIFICAND_BITS: u32 = $significand_bits;
-
-                fn is_nan(self) -> bool {
-                    self.is_nan()
-                }
-                fn is_infinite(self) -> bool {
-                    self.is_infinite()
-                }
-                fn to_bits(self) -> Self::Int {
-                    self.to_bits()
-                }
-                fn from_bits(bits: Self::Int) -> Self {
-                    Self::from_bits(bits)
-                }
-                fn signum(self) -> Self {
-                    self.signum()
-                }
-            }
-
-            impl Hex for $fty {
-                fn hex(self) -> String {
-                    self.to_bits().hex()
-                }
-            }
-        )+
-    }
-}
-
-impl_float!(
-    f32, u32, i32, 23;
-    f64, u64, i64, 52;
-);
-
-/// Common types and methods for integers.
-pub trait Int: Copy + fmt::Display + fmt::Debug + PartialEq<Self> {
-    type OtherSign: Int;
-    type Unsigned: Int;
-    const BITS: u32;
-    const SIGNED: bool;
-
-    fn signed(self) -> <Self::Unsigned as Int>::OtherSign;
-    fn unsigned(self) -> Self::Unsigned;
-    fn checked_sub(self, other: Self) -> Option<Self>;
-    fn abs(self) -> Self;
-}
-
-macro_rules! impl_int {
-    ($($ui:ty, $si:ty ;)+) => {
-        $(
-            impl Int for $ui {
-                type OtherSign = $si;
-                type Unsigned = Self;
-                const BITS: u32 = <$ui>::BITS;
-                const SIGNED: bool = false;
-                fn signed(self) -> Self::OtherSign {
-                    self as $si
-                }
-                fn unsigned(self) -> Self {
-                    self
-                }
-                fn checked_sub(self, other: Self) -> Option<Self> {
-                    self.checked_sub(other)
-                }
-                fn abs(self) -> Self {
-                    unimplemented!()
-                }
-            }
-
-            impl Int for $si {
-                type OtherSign = $ui;
-                type Unsigned = $ui;
-                const BITS: u32 = <$ui>::BITS;
-                const SIGNED: bool = true;
-                fn signed(self) -> Self {
-                    self
-                }
-                fn unsigned(self) -> $ui {
-                    self as $ui
-                }
-                fn checked_sub(self, other: Self) -> Option<Self> {
-                    self.checked_sub(other)
-                }
-                fn abs(self) -> Self {
-                    self.abs()
-                }
-            }
-
-            impl_int!(@for_both $si);
-            impl_int!(@for_both $ui);
-
-        )+
-    };
-
-    (@for_both $ty:ty) => {
-        impl Hex for $ty {
-            fn hex(self) -> String {
-                format!("{self:#0width$x}", width = ((Self::BITS / 4) + 2) as usize)
-            }
-        }
-
-        impl<Input> $crate::CheckOutput<Input> for $ty
-        where
-            Input: Hex + fmt::Debug,
-            SpecialCase: MaybeOverride<Input>,
-        {
-            fn validate<'a>(
-                self,
-                expected: Self,
-                input: Input,
-                ctx: &$crate::CheckCtx,
-            ) -> TestResult {
-                if let Some(res) = SpecialCase::check_int(input, self, expected, ctx) {
-                    return res;
-                }
-
-                anyhow::ensure!(
-                    self == expected,
-                    "\
-                    \n    input:    {input:?} {ibits}\
-                    \n    expected: {expected:<22?} {expbits}\
-                    \n    actual:   {self:<22?} {actbits}\
-                    ",
-                    actbits = self.hex(),
-                    expbits = expected.hex(),
-                    ibits = input.hex(),
-                );
-
-                Ok(())
-            }
-        }
-    }
-}
-
-impl_int!(
-    u32, i32;
-    u64, i64;
-);
-
-/// A helper trait to print something as hex with the correct number of nibbles, e.g. a `u32`
-/// will always print with `0x` followed by 8 digits.
-///
-/// This is only used for printing errors so allocating is okay.
-pub trait Hex: Copy {
-    fn hex(self) -> String;
-}
-
-impl<T1> Hex for (T1,)
-where
-    T1: Hex,
-{
-    fn hex(self) -> String {
-        format!("({},)", self.0.hex())
-    }
-}
-
-impl<T1, T2> Hex for (T1, T2)
-where
-    T1: Hex,
-    T2: Hex,
-{
-    fn hex(self) -> String {
-        format!("({}, {})", self.0.hex(), self.1.hex())
-    }
-}
-
-impl<T1, T2, T3> Hex for (T1, T2, T3)
-where
-    T1: Hex,
-    T2: Hex,
-    T3: Hex,
-{
-    fn hex(self) -> String {
-        format!("({}, {}, {})", self.0.hex(), self.1.hex(), self.2.hex())
-    }
-}
diff --git a/library/compiler-builtins/libm/crates/libm-test/src/test_traits.rs b/library/compiler-builtins/libm/crates/libm-test/src/test_traits.rs
index 34e15e0b286..67df83fb492 100644
--- a/library/compiler-builtins/libm/crates/libm-test/src/test_traits.rs
+++ b/library/compiler-builtins/libm/crates/libm-test/src/test_traits.rs
@@ -11,21 +11,7 @@ use std::fmt;
 
 use anyhow::{Context, bail, ensure};
 
-use crate::{Float, Hex, Int, MaybeOverride, SpecialCase, TestResult};
-
-/// Implement this on types that can generate a sequence of tuples for test input.
-pub trait GenerateInput<TupleArgs> {
-    fn get_cases(&self) -> impl Iterator<Item = TupleArgs>;
-}
-
-/// Trait for calling a function with a tuple as arguments.
-///
-/// Implemented on the tuple with the function signature as the generic (so we can use the same
-/// tuple for multiple signatures).
-pub trait TupleCall<Func>: fmt::Debug {
-    type Output;
-    fn call(self, f: Func) -> Self::Output;
-}
+use crate::{Float, Int, MaybeOverride, SpecialCase, TestResult};
 
 /// Context passed to [`CheckOutput`].
 #[derive(Clone, Debug, PartialEq, Eq)]
@@ -56,14 +42,38 @@ pub enum CheckBasis {
     Mpfr,
 }
 
+/// Implement this on types that can generate a sequence of tuples for test input.
+pub trait GenerateInput<TupleArgs> {
+    fn get_cases(&self) -> impl Iterator<Item = TupleArgs>;
+}
+
+/// Trait for calling a function with a tuple as arguments.
+///
+/// Implemented on the tuple with the function signature as the generic (so we can use the same
+/// tuple for multiple signatures).
+pub trait TupleCall<Func>: fmt::Debug {
+    type Output;
+    fn call(self, f: Func) -> Self::Output;
+}
+
 /// A trait to implement on any output type so we can verify it in a generic way.
 pub trait CheckOutput<Input>: Sized {
     /// Validate `self` (actual) and `expected` are the same.
     ///
     /// `input` is only used here for error messages.
-    fn validate<'a>(self, expected: Self, input: Input, ctx: &CheckCtx) -> TestResult;
+    fn validate(self, expected: Self, input: Input, ctx: &CheckCtx) -> TestResult;
+}
+
+/// A helper trait to print something as hex with the correct number of nibbles, e.g. a `u32`
+/// will always print with `0x` followed by 8 digits.
+///
+/// This is only used for printing errors so allocating is okay.
+pub trait Hex: Copy {
+    fn hex(self) -> String;
 }
 
+/* implement `TupleCall` */
+
 impl<T1, R> TupleCall<fn(T1) -> R> for (T1,)
 where
     T1: fmt::Debug,
@@ -143,72 +153,193 @@ where
     }
 }
 
-// Implement for floats
-impl<F, Input> CheckOutput<Input> for F
+/* implement `Hex` */
+
+impl<T1> Hex for (T1,)
 where
-    F: Float + Hex,
+    T1: Hex,
+{
+    fn hex(self) -> String {
+        format!("({},)", self.0.hex())
+    }
+}
+
+impl<T1, T2> Hex for (T1, T2)
+where
+    T1: Hex,
+    T2: Hex,
+{
+    fn hex(self) -> String {
+        format!("({}, {})", self.0.hex(), self.1.hex())
+    }
+}
+
+impl<T1, T2, T3> Hex for (T1, T2, T3)
+where
+    T1: Hex,
+    T2: Hex,
+    T3: Hex,
+{
+    fn hex(self) -> String {
+        format!("({}, {}, {})", self.0.hex(), self.1.hex(), self.2.hex())
+    }
+}
+
+/* trait implementations for ints */
+
+macro_rules! impl_int {
+    ($($ty:ty),*) => {
+        $(
+            impl Hex for $ty {
+                fn hex(self) -> String {
+                    format!("{self:#0width$x}", width = ((Self::BITS / 4) + 2) as usize)
+                }
+            }
+
+            impl<Input> $crate::CheckOutput<Input> for $ty
+            where
+                Input: Hex + fmt::Debug,
+                SpecialCase: MaybeOverride<Input>,
+            {
+                fn validate<'a>(
+                    self,
+                    expected: Self,
+                    input: Input,
+                    ctx: &$crate::CheckCtx,
+                ) -> TestResult {
+                    validate_int(self, expected, input, ctx)
+                }
+            }
+        )*
+    };
+}
+
+fn validate_int<'a, I, Input>(actual: I, expected: I, input: Input, ctx: &CheckCtx) -> TestResult
+where
+    I: Int + Hex,
     Input: Hex + fmt::Debug,
-    u32: TryFrom<F::SignedInt, Error: fmt::Debug>,
     SpecialCase: MaybeOverride<Input>,
 {
-    fn validate<'a>(self, expected: Self, input: Input, ctx: &CheckCtx) -> TestResult {
-        // Create a wrapper function so we only need to `.with_context` once.
-        let inner = || -> TestResult {
-            let mut allowed_ulp = ctx.ulp;
-
-            // If the tested function requires a nonstandard test, run it here.
-            if let Some(res) =
-                SpecialCase::check_float(input, self, expected, &mut allowed_ulp, ctx)
-            {
-                return res;
+    if let Some(res) = SpecialCase::check_int(input, actual, expected, ctx) {
+        return res;
+    }
+
+    anyhow::ensure!(
+        actual == expected,
+        "\
+        \n    input:    {input:?} {ibits}\
+        \n    expected: {expected:<22?} {expbits}\
+        \n    actual:   {actual:<22?} {actbits}\
+        ",
+        actbits = actual.hex(),
+        expbits = expected.hex(),
+        ibits = input.hex(),
+    );
+
+    Ok(())
+}
+
+impl_int!(u32, i32, u64, i64);
+
+/* trait implementations for floats */
+
+macro_rules! impl_float {
+    ($($ty:ty),*) => {
+        $(
+            impl Hex for $ty {
+                fn hex(self) -> String {
+                    format!(
+                        "{:#0width$x}",
+                        self.to_bits(),
+                        width = ((Self::BITS / 4) + 2) as usize
+                    )
+                }
             }
 
-            // Check when both are NaNs
-            if self.is_nan() && expected.is_nan() {
-                // By default, NaNs have nothing special to check.
-                return Ok(());
-            } else if self.is_nan() || expected.is_nan() {
-                // Check when only one is a NaN
-                bail!("real value != NaN")
+            impl<Input> $crate::CheckOutput<Input> for $ty
+            where
+                Input: Hex + fmt::Debug,
+                SpecialCase: MaybeOverride<Input>,
+            {
+                fn validate<'a>(
+                    self,
+                    expected: Self,
+                    input: Input,
+                    ctx: &$crate::CheckCtx,
+                ) -> TestResult {
+                    validate_float(self, expected, input, ctx)
+                }
             }
+        )*
+    };
+}
+
+fn validate_float<'a, F, Input>(actual: F, expected: F, input: Input, ctx: &CheckCtx) -> TestResult
+where
+    F: Float + Hex,
+    Input: Hex + fmt::Debug,
+    u32: TryFrom<F::SignedInt, Error: fmt::Debug>,
+    SpecialCase: MaybeOverride<Input>,
+{
+    // Create a wrapper function so we only need to `.with_context` once.
+    let inner = || -> TestResult {
+        let mut allowed_ulp = ctx.ulp;
 
-            // Make sure that the signs are the same before checing ULP to avoid wraparound
-            let act_sig = self.signum();
-            let exp_sig = expected.signum();
-            ensure!(act_sig == exp_sig, "mismatched signs {act_sig} {exp_sig}");
+        // If the tested function requires a nonstandard test, run it here.
+        if let Some(res) = SpecialCase::check_float(input, actual, expected, &mut allowed_ulp, ctx)
+        {
+            return res;
+        }
 
-            if self.is_infinite() ^ expected.is_infinite() {
-                bail!("mismatched infinities");
-            }
+        // Check when both are NaNs
+        if actual.is_nan() && expected.is_nan() {
+            // By default, NaNs have nothing special to check.
+            return Ok(());
+        } else if actual.is_nan() || expected.is_nan() {
+            // Check when only one is a NaN
+            bail!("real value != NaN")
+        }
 
-            let act_bits = self.to_bits().signed();
-            let exp_bits = expected.to_bits().signed();
+        // Make sure that the signs are the same before checing ULP to avoid wraparound
+        let act_sig = actual.signum();
+        let exp_sig = expected.signum();
+        ensure!(act_sig == exp_sig, "mismatched signs {act_sig} {exp_sig}");
 
-            let ulp_diff = act_bits.checked_sub(exp_bits).unwrap().abs();
+        if actual.is_infinite() ^ expected.is_infinite() {
+            bail!("mismatched infinities");
+        }
 
-            let ulp_u32 = u32::try_from(ulp_diff)
-                .map_err(|e| anyhow::anyhow!("{e:?}: ulp of {ulp_diff} exceeds u32::MAX"))?;
+        let act_bits = actual.to_bits().signed();
+        let exp_bits = expected.to_bits().signed();
 
-            ensure!(ulp_u32 <= allowed_ulp, "ulp {ulp_diff} > {allowed_ulp}",);
+        let ulp_diff = act_bits.checked_sub(exp_bits).unwrap().abs();
 
-            Ok(())
-        };
+        let ulp_u32 = u32::try_from(ulp_diff)
+            .map_err(|e| anyhow::anyhow!("{e:?}: ulp of {ulp_diff} exceeds u32::MAX"))?;
 
-        inner().with_context(|| {
-            format!(
-                "\
-                \n    input:    {input:?} {ibits}\
-                \n    expected: {expected:<22?} {expbits}\
-                \n    actual:   {self:<22?} {actbits}\
-                ",
-                actbits = self.hex(),
-                expbits = expected.hex(),
-                ibits = input.hex(),
-            )
-        })
-    }
+        ensure!(ulp_u32 <= allowed_ulp, "ulp {ulp_diff} > {allowed_ulp}",);
+
+        Ok(())
+    };
+
+    inner().with_context(|| {
+        format!(
+            "\
+            \n    input:    {input:?} {ibits}\
+            \n    expected: {expected:<22?} {expbits}\
+            \n    actual:   {actual:<22?} {actbits}\
+            ",
+            actbits = actual.hex(),
+            expbits = expected.hex(),
+            ibits = input.hex(),
+        )
+    })
 }
 
+impl_float!(f32, f64);
+
+/* trait implementations for compound types */
+
 /// Implement `CheckOutput` for combinations of types.
 macro_rules! impl_tuples {
     ($(($a:ty, $b:ty);)*) => {