rust-lang / chalk

An implementation and definition of the Rust trait system using a PROLOG-like logic solver
https://rust-lang.github.io/chalk/book/
Other
1.82k stars 180 forks source link

Prefer clauses from the environment #584

Open flodiebold opened 4 years ago

flodiebold commented 4 years ago

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)

nikomatsakis commented 4 years ago

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.

nikomatsakis commented 4 years ago

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.

flodiebold commented 4 years ago

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?

ANtlord commented 1 year ago

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?

Geo25rey commented 1 year ago

@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 Strings by rustc, are interpreted as {unknown} in chalk.