-
Notifications
You must be signed in to change notification settings - Fork 13k
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
Explain why a closure is FnOnce
in closure errors.
#42196
Conversation
Thanks for the pull request, and welcome! The Rust team is excited to review your changes, and you should hear from @eddyb (or someone else) soon. If any changes to this PR are deemed necessary, please add them as extra commits. This ensures that the reviewer can see what has changed since they last reviewed the code. Due to the way GitHub handles out-of-date commits, this should also make it reasonably obvious what issues have or haven't been addressed. Large or tricky changes may require several passes of review and changes. Please see the contribution instructions for more information. |
@@ -597,6 +597,9 @@ impl<'a, 'tcx> BorrowckCtxt<'a, 'tcx> { | |||
if let Ok(ty::ClosureKind::FnOnce) = | |||
ty::queries::closure_kind::try_get(self.tcx, DUMMY_SP, id) { | |||
err.help("closure was moved because it only implements `FnOnce`"); | |||
if let Some(&(_kind, Some(span))) = self.tables.closure_kinds.get( ) { |
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.
How do you find the node_id here?
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.
This would be the result of self.tcx.hir.as_local_node_id(id)
, I think -- we're basically looking things up based on the id of the closure. However, what we might want to do is just to include this information in the result of closure_kind::try_get()
above, or else make a separate query.
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.
(Also, @eddyb, why is this try_get()
anyhow? That seems...dubious to me?)
There is no "upvar" in the source - it's solely the fact that that a closure referred to a variable at least once. |
src/librustc/ty/context.rs
Outdated
pub closure_kinds: NodeMap<ty::ClosureKind>, | ||
/// Records the kind of each closure and the span of the variable that | ||
/// cause the closure to be this kind. | ||
pub closure_kinds: NodeMap<(ty::ClosureKind, Option<Span>)>, |
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.
Nit: I think what we want is to include the span of a variable, but perhaps more than that we would prefer to include the name as well, for later use in the error message (including just the node-id of the reference could suffice, but I'm a bit wary of including a node-id in this value, since it will make life harder later as we handle incremental etc).
src/librustc_typeck/check/upvar.rs
Outdated
@@ -289,7 +290,8 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { | |||
|
|||
// to move out of an upvar, this must be a FnOnce closure | |||
self.adjust_closure_kind(upvar_id.closure_expr_id, | |||
ty::ClosureKind::FnOnce); | |||
ty::ClosureKind::FnOnce, | |||
tcx.hir.span(upvar_id.var_id)); |
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 think I would just use guarantor.span
here.
src/librustc_typeck/check/upvar.rs
Outdated
@@ -303,7 +305,8 @@ impl<'a, 'gcx, 'tcx> AdjustBorrowKind<'a, 'gcx, 'tcx> { | |||
// to be a FnOnce closure to permit moves out | |||
// of the environment. | |||
self.adjust_closure_kind(upvar_id.closure_expr_id, | |||
ty::ClosureKind::FnOnce); | |||
ty::ClosureKind::FnOnce, | |||
tcx.hir.span(upvar_id.var_id)); |
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.
Also here, guarantor.span
seems good.
src/librustc_typeck/check/upvar.rs
Outdated
self.adjust_closure_kind(upvar_id.closure_expr_id, ty::ClosureKind::FnMut); | ||
self.adjust_closure_kind(upvar_id.closure_expr_id, | ||
ty::ClosureKind::FnMut, | ||
tcx.hir.span(upvar_id.var_id)); |
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.
here, in addition to passing in the &Note
, we can pass in the cmt
that the note originated from, and use that span. That should be the span of the variable reference.
In what sense unnecessary? The idea is to be able to give better error messages -- essentially, when we tell a user that a closure can only be Do you feel like the error message improvement is unnecessary, or do you have another idea of how to accomplish it? If the latter, I'm all ears. If the former, I'm curious why you think so. |
I mean that giving name or pointing to the definition is enough (of an improvement) compared to finding the actual uses. OTOH, EUV is already used to collect those uses during typeck now that I think of it so it should be doable. |
FnOnce
in closure errors.FnOnce
in closure errors.
@nikomatsakis This should now work |
810de2a
to
31bfdd7
Compare
src/test/ui/fn_once-moved.stderr
Outdated
@@ -1,12 +1,15 @@ | |||
error[E0382]: use of moved value: `debug_dump_dict` | |||
--> $DIR/fn_once-moved.rs:21:5 | |||
| | |||
16 | for (key, value) in dict { | |||
| ---- dict moved here |
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.
This is great! My only concern is that, when you read this message, the dict moved here
seems a bit out of the blue. I am torn between trying to give more context in this label vs trying to move the label down below.
Also, as a minor style nit, we should put dict
in backticks:
`dict` moved here
Basically, it seems highly likely to me that people will get confused between the closure being moved twice and variable dict
being moved twice.
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.
What do you mean by 'move the label down below'?
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 don't know =) I was imagining output like this. We do still support this style, though we've been moving away from it in many cases. This may be a case where it's worthwhile:
error[E0382]: use of moved value: `debug_dump_dict`
--> $DIR/fn_once-moved.rs:21:5
|
20 | debug_dump_dict();
| --------------- value moved here
21 | debug_dump_dict();
| ^^^^^^^^^^^^^^^ value used here after move
|
note: closure cannot be invoked more than once because it moves the variable `dict` out of its environment
|
16 | for (key, value) in dict {
| ---- dict moved here
I think that if you use span_note
it will produce something sort of like this? You may have to experiment a bit. (I'm also unclear on whether "help" or "note" is more appropriate here.)
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.
Ok I will try that. Also yeah, a note makes much more sense than a help here.
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 changed it to span_note
and it looks like:
error[E0382]: use of moved value: `debug_dump_dict`
--> src/test/ui/fn_once-moved.rs:21:5
|
20 | debug_dump_dict();
| --------------- value moved here
21 | debug_dump_dict();
| ^^^^^^^^^^^^^^^ value used here after move
|
= note: closure cannot be invoked more than once because it moves the variable `dict` out of its environment
note: `dict` moved here
--> src/test/ui/fn_once-moved.rs:16:29
|
16 | for (key, value) in dict {
| ^^^^
error: aborting due to previous error
The flow of the error message is indeed better, should I commit 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.
@tommyip yeah, seems like progress; one thing I would suggest though -- can we merge the two "notes"? e.g., by using "closure cannot be invoked more than once because it moves the variable dict
out of its environment" as the text for span_note
?
b24d465
to
c2f7e94
Compare
Updated |
@bors r+ Great! |
📌 Commit c2f7e94 has been approved by |
@bors rollup |
…matsakis Explain why a closure is `FnOnce` in closure errors. Issue: rust-lang#42065 @nikomatsakis Am I going the right direction with this? ~~I am stuck in a few bits:~~ ~~1. How to trace the code to get the upvar instead of the original variable's span?~~ ~~2. How to find the node id of the upvar where the move occured?~~
Use tracked data introduced in rust-lang#42196 to provide a better closure error message by showing why a closure implements `FnOnce`. ``` error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` --> $DIR/issue_26046.rs:4:19 | 4 | let closure = move || { | ___________________^ 5 | | vec 6 | | }; | |_____^ | note: closure is `FnOnce` because it moves the variable `vec` out of its environment --> $DIR/issue_26046.rs:5:9 | 5 | vec | ^^^ error: aborting due to previous error(s) ``` Fixes rust-lang#26046
Better closure error message Use tracked data introduced in #42196 to provide a better closure error message by showing why a closure implements `FnOnce`. ``` error[E0525]: expected a closure that implements the `Fn` trait, but this closure only implements `FnOnce` --> $DIR/issue_26046.rs:4:19 | 4 | let closure = move || { | ___________________^ 5 | | vec 6 | | }; | |_____^ | note: closure is `FnOnce` because it moves the variable `vec` out of its environment --> $DIR/issue_26046.rs:5:9 | 5 | vec | ^^^ error: aborting due to previous error(s) ``` Fixes #26046 r? @nikomatsakis cc @doomrobo
Fixes: #42065