chalk
chalk copied to clipboard
Prefer clauses from the environment
The following code compiles in rustc:
fn f<T: Into<String>>(u: T) {
let x = u.into();
x.as_str();
}
This works even though there are other (blanket) impls that could apply for the into()
call. rust-analyzer on the other hand can't resolve the type of x
because the T: Into<?>
goal is ambiguous. Note that if we have a struct S
that impls Into<String>
instead of the type parameter T
, we get an ambiguity error (see this playground).
This works because rustc eagerly selects clauses from the environment, before looking at other impls. I think the old recursive solver actually handled this, but we removed the code because we didn't have / come up with a test case for it.
(CC rust-analyzer/rust-analyzer#5514)
Yeah, the plan for chalk was to try and push a "more pure" approach, where we would consider it ambiguous, but return "guidance", and leave it up to the rust-analyzer to decide when to apply that guidance.
I would definitely prefer to try this approach (guidance) before anything else, it's great that we can use rust-analyzer to see how well it works.
So, would this be something like Guidance::Suggested
? And then RA would basically detect when trying to resolve x.as_str
that it can't make progress, and apply the suggested guidance?
Hello @nikomatsakis, I've done a little investigation on the interation of Rust Analyzer with Chalk to infer types. But it would be great to have a confirmation if I'm following the right way. Rust Analyzer utilizes the unification via InferenceTable::relate
. The method does a quick union via Unifier::relate
, then returns RelationResult::goals
which later is used in Rust Analyzer. At this point Unifier::relate
doesn't utilize Environment
, which has the data to infer the type of Into
. Is this correct?
@nikomatsakis I disagree with the "more pure"/"guidance" approach. IMHO chalk should try its best to match what ructc would do in this situation. In this case, that would be to eagerly infer that x
is a String
.
For example, this same principle applies to my use case:
mod some_lib {
pub fn a_to_b(b_compat: &(impl Into<String> + Clone)) -> impl Into<String> {
b_compat.clone()
}
}
fn main() {
let a = "8";
let b = some_lib::a_to_b(&a).into(); // this is type String but rust-analyzer says the type is {unknown}
let c = b.replace("8", "9");
println!("b = {}, c = {}", b, c);
}
a
is correctly inferred, but b
and c
, which are interpreted as String
s by rustc, are interpreted as {unknown}
in chalk.