-
Notifications
You must be signed in to change notification settings - Fork 12.8k
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
Infer whether a closure implements Fn
, FnMut
, etc based on what actions it takes
#21805
Infer whether a closure implements Fn
, FnMut
, etc based on what actions it takes
#21805
Conversation
r? @pcwalton (rust_highfive has picked a reviewer for you, use r? to override) |
cc @aturon |
Oh, a note to the eventual reviewer -- the commits are mostly independent, but there's a bit of back-and-forth because I realized my initial strategy was overly complex. In particular, I envisioned the "deferred resolutions" introduced in 9a85a17 as a more general thing, but I realized that because we don't have to worry about recursive closures for kind inference, we can simplify everything if we specialize the deferred resolutions to calls, which is done in f9e46a8. |
pub const tag_closure: uint = 0x96; | ||
pub const tag_closure_type: uint = 0x97; | ||
pub const tag_closure_kind: uint = 0x98; | ||
// GAP 0x94...0x98 |
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.
Could we not have this?
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'm not sure I follow. Not have a gap? Not comment on the gap? I'm not sure how to avoid the gap except for renumbering everything below, which seems kind of pointless? (Or is there something obvious I'm overlooking?)
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.
Forget about it, I'll remove it (the gap) whenever I touch this code again.
I took the liberty of finding a few nits - feel free to r=me unless you want a second review. Any reason why this PR doesn't disregard explicit kinds? Maybe error if inference and the explicit kind mismatches. |
No, I just ran out of time and decided to post it as is. Maybe I'll try it locally and see what happens. But it doesn't make sense to error: I actually expect inference to get different results than the expectation on a fairly regular basis. For example, if the expected kind is |
I was actually talking about |
@eddyb as an experiment, I tried removing all inference of expected kinds beyond what's implemented in this PR. I immediately hit some (minor) bugs around |
So I'm personally inclined to land it as is and patch those other problems later. |
f9e46a8
to
b31a3df
Compare
doing the final checking for closure calls until after trait inference is performed. This isn't important now, but it's essential if we are to delay inferring the closure kind.
…ity that its value is not yet known.
…they are borrowed or by value.
upvar inference. Upvar inference can cause some obligations to be deferred, notably things like `F : Sized` where `F` is a closure type, or `F : FnMut`. Adjust the ordering therefore so that we process all traits and apply fallback, do upvar inference, and only then start reporting errors for outstanding obligations.
generate the closure type and closure kind separately.
Trickier cases not yet handled.
specialized to closures, and invoke them as soon as we know the closure kind. I thought initially we would need a fixed-point inference algorithm but it appears I was mistaken, so we can do this.
…e weren't properly adjusting the closure kind in that case.
…s not yet known.
b31a3df
to
870aea2
Compare
@eddyb want to look over those last two commits? |
reassigning to eddyb since he did a review already |
…ddyb Currently, we only infer the kind of a closure based on the expected type or explicit annotation. If neither applies, we currently report an error. This pull request changes that case to defer the decision until we are able to analyze the actions of the closure: closures which mutate their environment require `FnMut`, closures which move out of their environment require `FnOnce`. This PR is not the end of the story: - It does not remove the explicit annotations nor disregard them. The latter is the logical next step to removing them (we'll need a snapshot before we can do anything anyhow). Disregarding explicit annotations might expose more bugs since right now all closures in libstd/rustc use explicit annotations or the expected type, so this inference never kicks in. - The interaction with instantiating type parameter fallbacks leaves something to be desired. This is mostly just saying that the algorithm from rust-lang/rfcs#213 needs to be implemented, which is a separate bug. There are some semi-subtle interactions though because not knowing whether a closure is `Fn` vs `FnMut` prevents us from resolving obligations like `F : FnMut(...)`, which can in turn prevent unification of some type parameters, which might (in turn) lead to undesired fallback. We can improve this situation however -- even if we don't know whether (or just how) `F : FnMut(..)` holds or not for some closure type `F`, we can still perform unification since we *do* know the argument and return types. Once kind inference is done, we can complete the `F : FnMut(..)` analysis -- which might yield an error if (e.g.) the `F` moves out of its environment. r? @nick29581
This needs a snapshot that includes #21805 before it can be merged. There are some places where type inference regressed after I removed the annotations (see `FIXME`s). cc @nikomatsakis. r? @eddyb or anyone (I'll remove the `FIXME`s before merging, as they are only intended to point out regressions)
Currently, we only infer the kind of a closure based on the expected type or explicit annotation. If neither applies, we currently report an error. This pull request changes that case to defer the decision until we are able to analyze the actions of the closure: closures which mutate their environment require
FnMut
, closures which move out of their environment requireFnOnce
.This PR is not the end of the story:
Fn
vsFnMut
prevents us from resolving obligations likeF : FnMut(...)
, which can in turn prevent unification of some type parameters, which might (in turn) lead to undesired fallback. We can improve this situation however -- even if we don't know whether (or just how)F : FnMut(..)
holds or not for some closure typeF
, we can still perform unification since we do know the argument and return types. Once kind inference is done, we can complete theF : FnMut(..)
analysis -- which might yield an error if (e.g.) theF
moves out of its environment.r? @nick29581