Type inference failure when accessing a value before its type is resolved

Hello,

I noticed that the compiler / Rust Analyzer behaves unexpectedly. Type inference fails if a value is accessed before the point where its type is ultimately inferred, as if the inference context is lost.

I will provide the steps to reproduce the bug. (The playground link can be found at the bottom)

Test struct :

#[derive(Clone, Debug)]
pub struct NodeMoonStatsDTO {
    pub node_id: String,
    pub uptime: u64,
    //.....
}

type SharedState = Arc<DashMap<String, DashMap<String, NodeMoonStatsDTO>>>;

pub struct SystemMonitorService {
    pub live_nodes_moon_stats: SharedState,
   //.....
}

Step 1: Type inference works correctly; result is properly inferred as Option<Vec<NodeMoonStatsDTO>>.

Here are the details of the variable types :

Step 2: Add the first building block before the bug appears.

So far, everything works correctly; no bug appears.

Step 3: Introduce a loop. :face_with_monocle:

However, it correctly knows that n is of type NodeMoonStatsDTO, yet at the same time it seems to ignore it.

This is only one example; I have encountered the same issue in several parts of the code.

Here is an example code with the same bug (I replaced DashMap with HashMap to simplify it).

:play_button: Here is the playground link.

:face_with_monocle: Here is the GitHub issue link


I use :

rustup: 1.29.0 
rustc : 1.95.0
rust-analyzer: 0.3.2887

Thank you in advance for your help and feedback.

1 Like

Does it always reproduce with rustc or is it sometimes only with Rust Analyzer?

Even with rustc :

> rustc main.rs 

error[E0282]: type annotations needed
  --> main.rs:28:28
   |
28 |             println!("{}", n.node_id);
   |                            ^ cannot infer type

error: aborting due to 1 previous error

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

You can test it using the playground link

Right, your playground demonstrated that. But your text says RA specifically. Do you have any reason to think it's RA specific?

Additionally, do you have any reason to think it's actually "recently"? I spot-checked the example back through 1.60 and didn't find a rustc version it worked on.

2 Likes

I initially thought it was caused by Rust Analyzer, but after testing directly with rustc (as you suggested), I encountered the same issue. This suggests it may be related to the compiler itself.

I think the reason I never encountered this bug before is that I usually specified return types explicitly. Since refactoring the code to make it less verbose, I’ve indirectly uncovered this issue.

This has nothing to do with the loop. I've seen this on many field accesses where the compiler can infer the struct type just fine. Here's a minimal example:

1 Like

Here is another minimal example similar to the original:

struct S {
    elem: i32
}

fn foo(a: Vec<S>) -> Vec<S> {
    let result = a.into_iter().collect();
    for s in &result {
        println!("{}", s.elem);
    }
    result
}
error[E0282]: type annotations needed
 --> src/lib.rs:8:24
  |
8 |         println!("{}", s.elem);
  |                        ^ cannot infer type

The problem is that field accesses and method calls have to be resolved based on information known before the call. This is just a limitation of the current type checker.

At println, the compiler knows that result is FromIterator<S>, and that &result is IntoIterator<Item=X> for some unknown type X. It only knows that s is of type X, and can't figure out what s.elem refers to because X is unknown.

Only later, in the last line (where we return result) does the compiler realize that result must be Vec<S> and thus that X = &S.

I think this could be fixed by having all the field accesses and method calls resolved later, when the necessary information becomes available after type checking the rest of the function.

2 Likes

@jplatte It’s essentially the same phenomenon, but triggered in a different way.

@tczajka Your example is indeed much clearer.


Working case:

A has an unknown type β†’ A = B β†’ B has a known type β†’ compiles successfully

Failing case:

A has an unknown type β†’ access A.field β†’ A = B β†’ B has a known type β†’ type inference fails

Even though the IDE (e.g. Rust Analyzer) resolves the type correctly, the compiler still reports a type inference error.

It seems that type inference loses track of the type if the value from A is accessed or manipulated before it is assigned to B.

This would be a welcome improvement. :roll_eyes:

I revised the post title and content (as well as the GitHub issue) so that the problem is no longer described as being specific to loops, but rather presented in a more general way.

Unfortunately the impact on compiler performance is unknown, and is likely to be large (unless someone will put a PR proving otherwise, of course). This likely will be rejected on that basis alone, given that the current state of things is not that bad.

rust-analyzer showing the type is because the type can be inferred at the end, and yes it's confusing, although I'm unsure how r-a could do better (displaying the type is definitely wanted, as it also impacts other IDE features such as inserting the type. Also, not displaying the type is quite difficult). This can even manifest in r-a errors (as "type Type does not have a method/field X", when it clearly has), and that is extra confusing and something we should fix.

2 Likes