Skip to content

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

Closed

Conversation

ShoyuVanilla
Copy link
Member

Currently, the Rust Analyzer infers the kind(Fn, FnMut or FnOnce) 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;
image
which is compiled and runs without any error on Rust compiler.

This PR gathers FnTrait predicates from obligations and uses them inside infer_closures to infer closure kinds, and fixes some wrong impl FnX inferences in preexisting test codes.

Fixes #16421

@rustbot rustbot added S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. has-merge-commits S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 1, 2024
@rustbot
Copy link
Collaborator

rustbot commented Feb 1, 2024

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:

$ # rebase
$ git rebase -i master
$ # delete any merge commits in the editor that appears
$ git push --force-with-lease

The following commits are merge commits:

@ShoyuVanilla ShoyuVanilla force-pushed the fix-closure-kind-infer branch from a155bec to 6936db4 Compare February 1, 2024 16:24
@rustbot rustbot removed has-merge-commits S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. labels Feb 1, 2024
@bors
Copy link
Contributor

bors commented Feb 8, 2024

☔ The latest upstream changes (presumably #16454) made this pull request unmergeable. Please resolve the merge conflicts.

@ShoyuVanilla ShoyuVanilla force-pushed the fix-closure-kind-infer branch from b52e9a4 to 9685108 Compare February 8, 2024 15:58
@ShoyuVanilla ShoyuVanilla changed the title Fix wrong closure kind inferences with closures with predicates Fix wrong closure kind inferences for closures with predicates Feb 8, 2024
Copy link
Member

@Veykril Veykril left a 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)
Copy link
Member

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

Copy link
Member Author

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

Comment on lines +150 to +186
fn_trait_predicates: Vec<(Ty, FnTrait)>,
cached_fn_trait_ids: Option<CachedFnTraitIds>,
Copy link
Member

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)

Copy link
Member Author

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

Copy link
Member Author

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

Copy link
Member Author

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

Copy link
Member Author

@ShoyuVanilla ShoyuVanilla Feb 16, 2024

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;

  1. We need to use "correct" pending obligations for closure kind inferencing
  2. 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 😅 )

@Veykril Veykril added S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author. and removed S-waiting-on-review Status: Awaiting review from the assignee but also interested parties. labels Feb 9, 2024
@ShoyuVanilla
Copy link
Member Author

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

Okay, I'll delve into it more

@bors
Copy link
Contributor

bors commented Feb 10, 2024

☔ The latest upstream changes (presumably #16527) made this pull request unmergeable. Please resolve the merge conflicts.

@ShoyuVanilla ShoyuVanilla changed the title Fix wrong closure kind inferences for closures with predicates fix: Wrong closure kind inferences for closures with predicates Feb 16, 2024
Fix existing errors in test cases

Fix another test

Fix another test

Fix lints

Remove irrelevant test code included by mistake
@ShoyuVanilla ShoyuVanilla force-pushed the fix-closure-kind-infer branch from 9685108 to 3229987 Compare February 17, 2024 06:38
@ShoyuVanilla
Copy link
Member Author

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)();
}

but this is not compiled;

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.
Sorry for wasting your time 😢

@Veykril
Copy link
Member

Veykril commented Feb 18, 2024

No problem, and thanks for investigating!

bors added a commit that referenced this pull request Feb 27, 2024
…kril

fix: Wrong closure kind deduction for closures with predicates

Completes #16472, fixes #16421

The changed closure kind deduction is mostly simlar to `rustc_hir_typeck/src/closure.rs`.
Porting closure sig deduction from it seems possible too and I'm considering doing it with another PR
@ShoyuVanilla ShoyuVanilla deleted the fix-closure-kind-infer branch July 12, 2024 23:00
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
S-waiting-on-author Status: This is awaiting some action (such as code changes or more information) from the author.
Projects
None yet
Development

Successfully merging this pull request may close these issues.

False positive "cannot mutate immutable variable (E0384)" error
4 participants