Skip to content
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

diagnostics: Note capturing closures can't be coerced to fns #78901

Merged
merged 1 commit into from
Jan 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions compiler/rustc_typeck/src/check/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
return;
}
self.suggest_no_capture_closure(err, expected, expr_ty);
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
self.suggest_missing_parentheses(err, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
Expand Down
34 changes: 33 additions & 1 deletion compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ use super::FnCtxt;
use crate::astconv::AstConv;

use rustc_ast::util::parser::ExprPrecedence;
use rustc_span::{self, Span};
use rustc_span::{self, MultiSpan, Span};

use rustc_errors::{Applicability, DiagnosticBuilder};
use rustc_hir as hir;
Expand Down Expand Up @@ -287,6 +287,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

/// When encountering a closure that captures variables, where a FnPtr is expected,
/// suggest a non-capturing closure
pub(in super::super) fn suggest_no_capture_closure(
estebank marked this conversation as resolved.
Show resolved Hide resolved
&self,
err: &mut DiagnosticBuilder<'_>,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) {
if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
// Report upto four upvars being captured to reduce the amount error messages
// reported back to the user.
let spans_and_labels = upvars
.iter()
.take(4)
.map(|(var_hir_id, upvar)| {
let var_name = self.tcx.hir().name(*var_hir_id).to_string();
let msg = format!("`{}` captured here", var_name);
(upvar.span, msg)
})
.collect::<Vec<_>>();

let mut multi_span: MultiSpan =
spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
for (sp, label) in spans_and_labels {
multi_span.push_span_label(sp, label);
}
err.span_note(multi_span, "closures can only be coerced to `fn` types if they do not capture any variables");
}
}
}

/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
&self,
Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/closures/closure-no-fn-1.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
|
= note: expected fn pointer `fn(u8) -> u8`
found closure `[closure@$DIR/closure-no-fn-1.rs:6:29: 6:50]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> $DIR/closure-no-fn-1.rs:6:39
|
LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
| ^ `a` captured here

error: aborting due to previous error

Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/closures/closure-no-fn-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ LL | let bar: fn() -> u8 = || { b };
|
= note: expected fn pointer `fn() -> u8`
found closure `[closure@$DIR/closure-no-fn-2.rs:6:27: 6:35]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> $DIR/closure-no-fn-2.rs:6:32
|
LL | let bar: fn() -> u8 = || { b };
| ^ `b` captured here

error: aborting due to previous error

Expand Down
8 changes: 8 additions & 0 deletions src/test/ui/closures/closure-no-fn-4.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
fn main() {
let b = 2;
let _: fn(usize) -> usize = match true {
true => |a| a + 1,
false => |a| a - b,
//~^ ERROR `match` arms have incompatible types
};
}
24 changes: 24 additions & 0 deletions src/test/ui/closures/closure-no-fn-4.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0308]: `match` arms have incompatible types
--> $DIR/closure-no-fn-4.rs:5:18
|
LL | let _: fn(usize) -> usize = match true {
| _________________________________-
LL | | true => |a| a + 1,
| | --------- this is found to be of type `fn(usize) -> usize`
LL | | false => |a| a - b,
| | ^^^^^^^^^ expected fn pointer, found closure
LL | |
LL | | };
| |_____- `match` arms have incompatible types
|
= note: expected fn pointer `fn(usize) -> usize`
found closure `[closure@$DIR/closure-no-fn-4.rs:5:18: 5:27]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> $DIR/closure-no-fn-4.rs:5:26
|
LL | false => |a| a - b,
| ^ `b` captured here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
12 changes: 12 additions & 0 deletions src/test/ui/closures/closure-no-fn-5.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
// When providing diagnostics about not being able to coerce a capturing-closure
// to fn type, we want to report only upto 4 captures.

fn main() {
let a = 0u8;
let b = 0u8;
let c = 0u8;
let d = 0u8;
let e = 0u8;
let bar: fn() -> u8 = || { a; b; c; d; e };
//~^ ERROR mismatched types
}
23 changes: 23 additions & 0 deletions src/test/ui/closures/closure-no-fn-5.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
error[E0308]: mismatched types
--> $DIR/closure-no-fn-5.rs:10:27
|
LL | let bar: fn() -> u8 = || { a; b; c; d; e };
| ---------- ^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
| |
| expected due to this
|
= note: expected fn pointer `fn() -> u8`
found closure `[closure@$DIR/closure-no-fn-5.rs:10:27: 10:47]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> $DIR/closure-no-fn-5.rs:10:32
|
LL | let bar: fn() -> u8 = || { a; b; c; d; e };
| ^ ^ ^ ^ `d` captured here
| | | |
| | | `c` captured here
| | `b` captured here
| `a` captured here

error: aborting due to previous error

For more information about this error, try `rustc --explain E0308`.
5 changes: 5 additions & 0 deletions src/test/ui/closures/closure-reform-bad.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ LL | call_bare(f)
|
= note: expected fn pointer `for<'r> fn(&'r str)`
found closure `[closure@$DIR/closure-reform-bad.rs:10:13: 10:50]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> $DIR/closure-reform-bad.rs:10:43
|
LL | let f = |s: &str| println!("{}{}", s, string);
| ^^^^^^ `string` captured here

error: aborting due to previous error

Expand Down
5 changes: 5 additions & 0 deletions src/test/ui/closures/print/closure-print-verbose.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,11 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
|
= note: expected fn pointer `fn(u8) -> u8`
found closure `[main::{closure#0} closure_substs=(unavailable)]`
note: closures can only be coerced to `fn` types if they do not capture any variables
--> $DIR/closure-print-verbose.rs:10:39
|
LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
| ^ `a` captured here

error: aborting due to previous error

Expand Down