Stream: t-compiler/rust-analyzer

Topic: Chalk hang

Florian Diebold (Sep 03 2019 at 15:28, on Zulip):

@nikomatsakis @matklad I've hacked together some code to print the stuff we're passing to Chalk (here), and here's the beginning of the output for the actix-web hang: gist

you can see it's starting with the goal Implements(?0: ServiceFactory<TcpStream>), and then enumerating kind of the whole world because the first where clause of the impl never applies (but it starts with the last). Although it seems like this would be problematic even if there was some solution for the first where clause.

The thing is, we wouldn't even need to solve this: Even if it had a unique solution, rustc wouldn't infer that. So are we missing some short-circuit behavior here? (OTOH it seems that even if RA somehow avoided trying to solve this goal, it could easily happen while solving some other goal...)

nikomatsakis (Sep 03 2019 at 21:25, on Zulip):

it's true that rustc doesn't solve these sorts of cases, though I would like it to (I think...)

nikomatsakis (Sep 04 2019 at 22:24, on Zulip):

ok, i'm circling back to this

nikomatsakis (Sep 04 2019 at 22:25, on Zulip):

it looks like it's trying to solve the T: NewService, I guess this is what you meant?

Florian Diebold (Sep 05 2019 at 06:55, on Zulip):


Florian Diebold (Sep 06 2019 at 20:25, on Zulip):

Someone posted another example here. The output shows it's trying to enumerate Copy and Ord types, which there aren't actually many because we don't handle derives yet and don't manage to expand the macros in the standard library enough to get the impls for the primitive types, but there are enough impls to make it search a long time...

Florian Diebold (Sep 07 2019 at 16:39, on Zulip):

it seems to me this should be fixable with a smarter selection of the next subgoal, i.e. chalk#80. It would have to be smart enough to select a good clause when it sees I: Bounded, I: Zero, I: DivAssign<Self>, I: Ord, I: Copy though, so maybe it would need to check how many impls there are for each of these... or maybe it could even try another subgoal if it takes too long one the current one? :thinking:

Florian Diebold (Sep 07 2019 at 17:43, on Zulip):

the last thing would be a kind of 'fuel for subgoals', actually :thinking:

Florian Diebold (Sep 08 2019 at 14:24, on Zulip):

I'm now running into another problem with ?0: Trait goals: I'm giving types to closures; each closure gets its own type, and that type should implement the Fn traits. But what I then do when Chalk asks for impls for Fn? I would have to iterate all bodies in the whole crate to list all closures, because the solver is shared for the whole crate. (I could maybe make the solver per-def, but that doesn't seem like a good idea...)

Now Chalk could of course pass a hint about the self type (I tried that), but actually at first it doesn't know the self type (it's trying to solve ?0: Fn), and if I don't return the impl in that call I think it caches that and doesn't try again later with a more specific type. So it seems to me I would need some kind of way of saying ?0: Fn is always ambiguous, and we should only try to solve it when we have more information...

@nikomatsakis what do you think would be the 'correct' approach for this?

nikomatsakis (Sep 09 2019 at 17:46, on Zulip):

Hey @Florian Diebold -- let me read in detail your posts -- but I was giving this some thought over the weekend.

Overall, this was the sort of scenario that I had hoped to use the "non-enumerable" concept to control.

To start, we could certainly say that a goal like ?T: Foo for any trait Foo is non-enumerable, much as rustc does today.

nikomatsakis (Sep 09 2019 at 17:47, on Zulip):

I'm not wild about it, because it's an arbitrary limitation, but it might be a practical step

nikomatsakis (Sep 09 2019 at 17:47, on Zulip):

The other obvious thing is that yes I had expected us to get smarter about controlling our selection order

matklad (Sep 09 2019 at 17:49, on Zulip):

because it's an arbitrary limitation

I think limitations which clearly delineate the scope of the search can be good :) Like, allowing pub impls in function bodies would be a useful limitation.

nikomatsakis (Sep 09 2019 at 17:49, on Zulip):

I agree, I just don't like that one

nikomatsakis (Sep 09 2019 at 17:49, on Zulip):

but I still think it'd be good to impose it for now

nikomatsakis (Sep 09 2019 at 17:51, on Zulip):

(My specific reason not to like that one is that it interferes with the design I would prefer for the Try trait, making some kinds of inference more difficult there)

nikomatsakis (Sep 09 2019 at 17:51, on Zulip):

In any case, I think reproducing rustc's logic for now is fine, and I like that we have the means to talk more carefully about what we want the rules to be

nikomatsakis (Sep 09 2019 at 17:51, on Zulip):

It'd be good to know if there are other problems once we take that step :)

nikomatsakis (Sep 09 2019 at 19:26, on Zulip):

To start, we could certainly say that a goal like ?T: Foo for any trait Foo is non-enumerable, much as rustc does today.

So I realize that we can do this -- and perhaps should do this -- on the rust-analyzer side. Just opened rust-analyzer#1800.

Florian Diebold (Sep 09 2019 at 19:30, on Zulip):

Will this mean Chalk will return an ambiguous result if it has to solve such a goal, or just that it'll try those goals last?

Florian Diebold (Sep 09 2019 at 19:36, on Zulip):

ah, from reading the code, the former :)

nikomatsakis (Sep 09 2019 at 19:50, on Zulip):

@Florian Diebold well sort of both

nikomatsakis (Sep 09 2019 at 19:51, on Zulip):

it will re-order such tasks last (dynamically, though, but we can improve that), but if ultimately it has to solve such a thing, it will return ambig

Last update: Jul 28 2021 at 03:15UTC