weiznich / rust-foundation-community-grant

20 stars 6 forks source link

recursion limit when using generic number types in easy-ml #6

Closed Skeletonxf closed 2 years ago

Skeletonxf commented 2 years ago

In Easy-ML I have traits that allow working with different number types like (f32, f64, i32) generically and I get overflow evaluating the requirement errors sometimes if the generic type isn't specified explicitly.

Minimal example:

#[test]
fn test_error() {
    use easy_ml::matrices::Matrix;
    let matrix = Matrix::from(vec![
        vec![ 1, 2 ],
        vec![ 3, 4 ]
    ]);
    let determinant = easy_ml::linear_algebra::determinant(&matrix);
}

Output

error[E0275]: overflow evaluating the requirement `for<'a> &'a Simd<_, {_: usize}>: Div<Simd<_, {_: usize}>>`
   --> tests/test_error.rs:8:23
    |
8   |     let determinant = easy_ml::linear_algebra::determinant(&matrix);
    |                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    |
    = help: consider increasing the recursion limit by adding a `#![recursion_limit = "256"]` attribute to your crate (`test_error`)
    = note: required because of the requirements on the impl of `for<'a> NumericByValue<Simd<_, {_: usize}>, Simd<_, {_: usize}>>` for `&'a Simd<_, {_: usize}>`
    = note: required because of the requirements on the impl of `for<'a> NumericRef<Simd<_, {_: usize}>>` for `&'a Simd<_, {_: usize}>`
    = note: required because of the requirements on the impl of `for<'a> Div<Trace<Simd<_, {_: usize}>>>` for `&'a Trace<Simd<_, {_: usize}>>`
    = note: 125 redundant requirements hidden
    = note: required because of the requirements on the impl of `for<'a> NumericRef<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Simd<_, {_: usize}>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>` for `&'a Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Trace<Simd<_, {_: usize}>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>`
note: required by a bound in `determinant`
   --> /home/skeletonxf/Documents/Rust/easy-ml/src/linear_algebra.rs:201:20
    |
201 |     for<'a> &'a T: NumericRef<T>,
    |                    ^^^^^^^^^^^^^ required by this bound in `determinant`

For more information about this error, try `rustc --explain E0275`.

The signature of determinant is

pub fn determinant<T: Numeric>(matrix: &Matrix<T>) -> Option<T>
where
    for<'a> &'a T: NumericRef<T>,

https://docs.rs/easy-ml/1.8.1/easy_ml/numeric/trait.Numeric.html https://docs.rs/easy-ml/1.8.1/easy_ml/numeric/trait.NumericRef.html https://docs.rs/easy-ml/1.8.1/easy_ml/linear_algebra/fn.determinant.html

I'm not really sure why the compiler isn't able to just tell me to specify T. I have two types Trace and Record that both implement Numeric and are generic over Numeric types themselves that might have something to do with the compiler getting stuck in recursion, but the standard library also has Wrapping and I've never seen this problem with that type.

Strangely if I call a method defined on Matrix that calls into determinant the compiler is perfectly happy even without me explicitly specifying what concrete number type I'm using.

#[test]
fn test_error() {
    use easy_ml::matrices::Matrix;
    let matrix = Matrix::from(vec![
        vec![ 1, 2 ],
        vec![ 3, 4 ]
    ]);
    let determinant = matrix.determinant();
}
impl<T: Numeric> Matrix<T>
where
    for<'a> &'a T: NumericRef<T>,
{
    pub fn determinant(&self) -> Option<T> {
        linear_algebra::determinant::<T>(self)
    }
}

https://docs.rs/easy-ml/1.8.1/easy_ml/matrices/struct.Matrix.html#method.determinant

weiznich commented 2 years ago

Thanks for the report, tracked this as 0554e827b1308f94b15e8295f3c5a71cf4637cab