Defining an unrelated type leads to recursion limit error

I do not understand why defining the Add operator for AD (in the code below) leads to an overflow evaluting requirements in a statement that I think is in no way using AD:

/*
If I execute 'cargo run', I get:
    Finished `dev` profile [unoptimized + debuginfo] target(s) in 0.01s
     Running `target/debug/package`
Hello World

If I execut 'cargo test', I get:
   Compiling package v0.1.0 (/home/bradbell/trash/rust/package)
error[E0275]: overflow evaluating the requirement `for<'a> &'a Simd<_, _>: Add`
  --> src/main.rs:65:24
   |
65 |         forward_0    : add_fun,
   |                        ^^^^^^^
   |
   = help: consider increasing the recursion limit by adding
*/
// -------------------------------------------------------------------------
// AD
pub struct AD<F> {
    pub value : F,
}
//
#[cfg(test)]
impl<F> std::ops::Add< &AD<F> > for &AD<F>
where
    for<'a> &'a F: std::ops::Add<&'a F, Output=F>,
    F    : Clone
{   type Output = AD<F>;
    //
    fn add(self , rhs : &AD<F> ) -> AD<F>
    {
        AD{ value : rhs.value.clone() }
    }
}
// AD is not used below here
// -------------------------------------------------------------------------
//
// LazyLock
use std::sync::LazyLock;
//
// FunType
pub type FunType<V> = fn(
    _var_zero : &mut Vec<V> ,
);
//
// add_fun
fn add_fun<V> ( var_zero : &mut Vec<V> )
where for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
{
    var_zero[2] = &var_zero[0] + &var_zero[1];
}
//
// FunInfo
#[derive(Clone)]
pub struct FunInfo<V> {
    pub forward_0 : FunType<V>,
}
//
// fun_init
pub fn fun_init<V>() -> FunInfo<V>
where for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
{
    FunInfo {
        forward_0    : add_fun,
    }
}
//
// GlobalData
pub trait GlobalData
where Self : Sized + 'static,
{
    fn get() -> &'static std::sync::LazyLock< FunInfo<Self> >;
}
impl GlobalData for f32 {
    fn get() -> &'static LazyLock< FunInfo<f32> > {
        pub static GLOBAL_DATA :
            LazyLock< FunInfo<f32> > = LazyLock::new( || fun_init() );
        &GLOBAL_DATA
    }
}
//
// main()
fn main() {
    println!("Hello World");
}
1 Like

FWIW, the code runs fine if you remove the HRTB on F in impl Add.

During the simplification, I mistakenly dropped the need for the where clause in the impl. Try this for the impl of Add for AD:

#[cfg(test)]
impl<F> std::ops::Add< &AD<F> > for &AD<F>
where
    for<'a> &'a F : std::ops::Add< &'a F, Output = F>,
    F    : Clone
{   type Output = AD<F>;
    //
    fn add(self , rhs : &AD<F> ) -> AD<F>
    {   AD {
            value : &self.value + &rhs.value
        }
    }
}

the snippet compiles with -Znext-solver. (godbolt link)

possibly related:

I'm not an expert on the trait solver, I can't explain the error, but for this particular example, on stable rust, you can "help" type inference with some type annotation (godbolt link):

pub fn fun_init<V>() -> FunInfo<V>
where for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
{
    FunInfo {
-        forward_0    : add_fun,
+        forward_0    : add_fun::<V>,
    }
}

impl GlobalData for f32 {
    fn get() -> &'static LazyLock< FunInfo<f32> > {
        pub static GLOBAL_DATA :
-            LazyLock< FunInfo<f32> > = LazyLock::new( || fun_init() );
+            LazyLock< FunInfo<f32> > = LazyLock::new( || fun_init::<f32>() );
        &GLOBAL_DATA
    }
}
1 Like

Thanks for the suggestion, I will try it in my original source problem and see if it works there.

The following is a better example of my actual application:

/ AD
#[derive(Debug)]
pub struct AD<V> {
    pub value : V,
}
//
impl<V> std::ops::Add< &AD<V> > for &AD<V>
where
    for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
{   type Output = AD<V>;
    //
    fn add(self , rhs : &AD<V> ) -> AD<V>
    {   AD { value : &self.value + &rhs.value } }
}
//
// FunType
pub type FunType<V> = fn( _var_zero : &mut Vec< AD<V> > );
//
// add_fun
fn add_fun<V> ( var_zero : &mut Vec< AD<V> > )
where for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
{ var_zero[2] = &var_zero[0] + &var_zero[1]; }
//
// FunInfo
#[derive(Clone)]
pub struct FunInfo<V> {
    pub fun : FunType<V>,
}
//
// fun_vec
pub fn fun_info<V>() -> FunInfo<V>
where for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
{ FunInfo { fun : add_fun::<V> } }
//
// main()
fn main() {
    let info                           = fun_info::<f32>();
    let fun                            = info.fun;
    let mut var_zero  : Vec< AD<f32> > = Vec::new();
    var_zero.push( AD{ value : 2f32 } );
    var_zero.push( AD{ value : 3f32 } );
    var_zero.push( AD{ value : f32::NAN } );
    println!("var_zero = {:?}", var_zero);
    fun(&mut var_zero);
    println!("var_zero = {:?}", var_zero);
}

I am once again stuck trying to fix the where clauses in my code. I have isolated the problem I am currently having in the file main.rs bleow. If I execute cargo run, the program runs correctly.
If I execute cargo test, I get the error message below the program, which does not help me know what to do to fix it (Note the #[cfg(test)] int main.rs below ) ?

main.rs

// AD
#[derive(Debug)]
pub struct AD<V> {
    pub value : V,
}
//
impl<V> std::ops::Add< &AD<V> > for &AD<V>
where
    for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
{   type Output = AD<V>;
    //
    fn add(self , rhs : &AD<V> ) -> AD<V>
    {   AD { value : &self.value + &rhs.value } }
}
//
// ---------------------------------------------------------------------------
// FunType
pub type FunType<V> = fn( _var_zero : &mut Vec< AD<V> > );

// hello_world
/// default [FunType] function will panic
fn hello_world<V> (
    _var_zero : &mut Vec< AD<V> > ,
) { println!("Hello World"); }
// ---------------------------------------------------------------------------
/// Information for one operator
#[derive(Clone)]
pub struct FunInfo<V> {
    pub fun : FunType<V>,
}
// ---------------------------------------------------------------------------
// get_op_info
pub fn get_op_info<V>() -> Vec< FunInfo<V> >
where
    for<'a> &'a V : std::ops::Add<&'a V, Output = V> ,
{   
    let hello = FunInfo { fun : hello_world::<V> };
    let op_info : Vec< FunInfo<V> > = vec![hello ];
    #[cfg(test)]
    set_op_info(&mut op_info);
    op_info
}
// --------------------------------------------------------------------------
// add_fun
fn add_fun<V> ( var_zero : &mut Vec< AD<V> > )
where for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
{ var_zero[2] = &var_zero[0] + &var_zero[1]; }
// ---------------------------------------------------------------------------
// set_op_info
pub fn set_op_info<V>( op_info : &mut Vec< FunInfo::<V> > ) 
where
    for<'a> &'a V : std::ops::Add<&'a V, Output = V> ,
{
    op_info[0] = FunInfo{ fun  : add_fun::<V> };
}
// ---------------------------------------------------------------------------
// main()
fn main() {
    let info                           = get_op_info::<f32>();
    let fun                            = info[0].fun;
    let mut var_zero  : Vec< AD<f32> > = Vec::new();
    var_zero.push( AD{ value : 2f32 } );
    var_zero.push( AD{ value : 3f32 } );
    var_zero.push( AD{ value : f32::NAN } );
    println!("var_zero = {:?}", var_zero);
    fun(&mut var_zero);
    println!("var_zero = {:?}", var_zero);
}

Error message:
= help: consider increasing the recursion limit by adding a #![recursion_limit = "256"] attribute to your crate (package)
note: required for &'a AD<Simd<_, _>> to implement for<'a> Add
--> src/main.rs:7:9
|
7 | impl std::ops::Add< &AD > for &AD
| ^^^^^^^^^^^^^^^^^^^^^^^ ^^^^^^
8 | where
9 | for<'a> &'a V : std::ops::Add< &'a V, Output = V>,
| ---------- unsatisfied trait bound introduced here
= note: 126 redundant requirements hidden
= note: required for &AD<AD<AD<AD<AD<AD<AD<AD<AD<AD<AD<AD<AD<AD<AD<AD<...>>>>>>>>>>>>>>>> to implement for<'a> Add
note: required by a bound in set_op_info
--> src/main.rs:52:21
|
50 | pub fn set_op_info( op_info : &mut Vec< FunInfo:: > )
| ----------- required by a bound in this function
51 | where
52 | for<'a> &'a V : std::ops::Add<&'a V, Output = V> ,
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ required by this bound in set_op_info
= note: the full name for the type has been written to '/home/bradbell/trash/rust/package/target/debug/deps/package-2e9b0110440db6ae.long-type-16883548437017512517.txt'
= note: consider using --verbose to print the full type name to the console

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

since it's the same issue cause by the bad interaction between the trait solver and type inference, the same workaround should work for this too, turbofish to the rescue.

-    set_op_info(&mut op_info);
+    set_op_info::<V>(&mut op_info);
1 Like

That worked, once I aded mut to the line
let mut op_info : Vec< FunInfo<V> > = vec![hello ];
Similar changes also fixed the code I am working on.

I guess that, when possible, I should specify the type in generic function calls.

Thanks for your help !

This topic was automatically closed 90 days after the last reply. We invite you to open a new topic if you have further questions or comments.