-
Notifications
You must be signed in to change notification settings - Fork 1.8k
fix: Wrong closure kind inferences for closures with predicates #16472
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: Wrong closure kind inferences for closures with predicates #16472
Conversation
There are merge commits (commits with multiple parents) in your changes. We have a no merge policy so these commits will need to be removed for this pull request to be merged. You can start a rebase with the following commands:
The following commits are merge commits: |
a155bec
to
6936db4
Compare
☔ The latest upstream changes (presumably #16454) made this pull request unmergeable. Please resolve the merge conflicts. |
b52e9a4
to
9685108
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The compiler also inspects the expected type first and foremost, and only then does it deduce_closure_signature_from_predicates if the expected type is a type variable or opaque alias. We probably should do the same (as much as we can, given our infra is not the same), https://github.com/rust-lang/rust/blob/11f32b73e0dc9287e305b5b9980d24aecdc8c17f/compiler/rustc_hir_typeck/src/closure.rs#L214
// // Diagnostics here, like compiler does in | ||
// // https://github.com/rust-lang/rust/blob/11f32b73e0dc9287e305b5b9980d24aecdc8c17f/compiler/rustc_hir_typeck/src/upvar.rs#L264 | ||
// } | ||
predicate.unwrap_or(from_capture) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should use unwrap_or_else(|| self.closure_kind_from_capture())
for lazyiness
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I was planning to implement a diagnostic here as the comments above says. I'll try it and will convert into unwrap_or_else
if it is not possible
fn_trait_predicates: Vec<(Ty, FnTrait)>, | ||
cached_fn_trait_ids: Option<CachedFnTraitIds>, |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not too happy with this, can't we just iterate through the pending obligations when necessary? (as is what rustc does)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The chalk resolves obligations like FnOnce
immediately, so they are not remained in the pending obligations. The most problematic thing is that it infers the closures with FnOnce
predicate as implementing FnMut
and Fn
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Maybe I should look into it more, but I'm not sure that it is intended chalk functionality or not
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Anyway, I'm gonna take a deep look into the chalk closure inferences. I might fix this from upstream or maybe RA is asking chalk without some details
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Veykril I had a closer into RA and chalk, and I think that we couldn't use pending obligations like rustc does.
When the chalk has to solve a goal that whether some closure implements some FnTrait
;
1. It asks RustIrDatabase
for the kind of our closure and checkes if the kind of closure is compatible with the given FnTrait
2. If so, it pushes the clause for the "FACT" that our closure implements that trait.
So, solving whether the given closure implements FnTrait
is heavily depends on RustIrDatabase
and I think that's reasonable for it to do so because what chalk wants to be(though it is not going to be actively maintained due to new trait solver in rustc) is an abstract trait solver, not a very definite rustc inference maker.
But since RA's ChalkContext
, which is our RustIrDatabase
implementation, always returns Fn
as the kind of a closure. (I think that this is sort of a heuristic to avoid having other inferences and coercions delayed or unresolved.)
Thus, the obligations like closure{n}[...] implements FnX
are always solved into Unique
solutions and not remain as "pending" because they are immediately solved.
Giving the "correct" kind of a closure doesn't work for this because;
- We need to use "correct" pending obligations for closure kind inferencing
- But the "correct" pending obligations are what we are going to do with "correct" kind of a closure, a cyclic dependency!
I've tried just giving FnOnce
as a closure_kind
but aside from that it makes lots of inferences in our test cases unresolved - if the later were to worked correctly I would have tried fixing those inferences -, it still does not "pend" obligations.
The obligations for closure implementation FnMut
and Fn
are solved as None
, and they are not pended as well.
So, I have concluded that we would have to modify lots of RA inferencing code to do this as rustc does, and this was why I wrote somewhat sad codes for similar thing 😢 (When I wrote it, I hadn't debugged the chalk much, but tried using pending obligations and it was always empty; so I had similar conclusion without much evidence then 😅 )
Okay, I'll delve into it more |
☔ The latest upstream changes (presumably #16527) made this pull request unmergeable. Please resolve the merge conflicts. |
Fix existing errors in test cases Fix another test Fix another test Fix lints Remove irrelevant test code included by mistake
9685108
to
3229987
Compare
After some investigations, I found out that I had made some terrible misunderstandings on what rustc does this 😱; While this is compiled and runs as my previous understandings for this issue; struct Foo<T: FnOnce()>(T);
fn main() {
let mut x = 0;
// let a = || { x = 1; };
let b = Foo(|| { x = 1 });
(b.0)();
} struct Foo<T: FnOnce()>(T);
fn main() {
let mut x = 0;
let a = || { x = 1; };
let b = Foo(a);
(b.0)();
} contrary to my previous misunderstandings, as seen in my wrong test cases. I'll close this and soon reopen new PR with proper fix, and I think that I could fix the thing in similar way the rustc does, as you said. |
No problem, and thanks for investigating! |
Currently, the Rust Analyzer infers the kind(

Fn
,FnMut
orFnOnce
) of a closure only with its captures.But the Rust compiler utilizes obligations and predicates(with here) to infer its kind whenever available (for example, coercing into a bounded argument), and only uses capture informations when not.
So, this makes false positive errors as we can see in #16421,
or more simpler cases like;
which is compiled and runs without any error on Rust compiler.
This PR gathers
FnTrait
predicates from obligations and uses them insideinfer_closures
to infer closure kinds, and fixes some wrongimpl FnX
inferences in preexisting test codes.Fixes #16421