In a very general sense, rustc can look at uses of a particular value to "constrain" the types it can be. If, at the end of this process, there isn't a concrete type known, you get an error:
fn foo() {
let x = vec![]; // error, type is Vec<_>
}
However, you could add usage to the function to "provide information to the compiler":
fn foo() {
let mut x = vec![]; // must be a Vec<&str>
x.push("hello");
}
It happens that, in Rust, functions are considered "boundaries" for type inference, so you can't write something like:
fn accepts_any_vec(mut v: Vec<_>) {
v.push("hello");
}
even though there's enough info here to work out the concrete type. This is a design decision, more than anything, and helps prevent details about the implementation of a function "leaking" unintentionally.
In your case, because you pass x: Overload::<_>::overload()
into a function that expects an i32
, that adds the bound:
i32: Overload::<_>::overload()
This case is pretty trivial, but it gets more complicated when you add things like associated types and other Rust goodies.
For more information on how this works, I'd recommend looking at the chalk project, which is an attempt to describe trait resolution in terms of logic rules (like the one above). It's not currently used in rustc but there are plans to switch over to it, though I'm not sure the status of that.
For more info on the style of type inference Rust does more generally, have a look at "Hindley-Milner type inference", it's not exactly what Rust uses, but it's pretty close.