Skip to content

Commit 45ba015

Browse files
authored
Rollup merge of #78901 - arora-aman:fix_closure_coerce, r=estebank
diagnostics: Note capturing closures can't be coerced to fns Fixes #72457, fixes #71895 r? `@estebank`
2 parents 058a710 + 1841a5f commit 45ba015

10 files changed

+121
-1
lines changed

compiler/rustc_typeck/src/check/demand.rs

+1
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,7 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
3232
if self.suggest_calling_boxed_future_when_appropriate(err, expr, expected, expr_ty) {
3333
return;
3434
}
35+
self.suggest_no_capture_closure(err, expected, expr_ty);
3536
self.suggest_boxing_when_appropriate(err, expr, expected, expr_ty);
3637
self.suggest_missing_parentheses(err, expr);
3738
self.note_need_for_fn_pointer(err, expected, expr_ty);

compiler/rustc_typeck/src/check/fn_ctxt/suggestions.rs

+33-1
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ use super::FnCtxt;
22
use crate::astconv::AstConv;
33

44
use rustc_ast::util::parser::ExprPrecedence;
5-
use rustc_span::{self, Span};
5+
use rustc_span::{self, MultiSpan, Span};
66

77
use rustc_errors::{Applicability, DiagnosticBuilder};
88
use rustc_hir as hir;
@@ -287,6 +287,38 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
287287
}
288288
}
289289

290+
/// When encountering a closure that captures variables, where a FnPtr is expected,
291+
/// suggest a non-capturing closure
292+
pub(in super::super) fn suggest_no_capture_closure(
293+
&self,
294+
err: &mut DiagnosticBuilder<'_>,
295+
expected: Ty<'tcx>,
296+
found: Ty<'tcx>,
297+
) {
298+
if let (ty::FnPtr(_), ty::Closure(def_id, _)) = (expected.kind(), found.kind()) {
299+
if let Some(upvars) = self.tcx.upvars_mentioned(*def_id) {
300+
// Report upto four upvars being captured to reduce the amount error messages
301+
// reported back to the user.
302+
let spans_and_labels = upvars
303+
.iter()
304+
.take(4)
305+
.map(|(var_hir_id, upvar)| {
306+
let var_name = self.tcx.hir().name(*var_hir_id).to_string();
307+
let msg = format!("`{}` captured here", var_name);
308+
(upvar.span, msg)
309+
})
310+
.collect::<Vec<_>>();
311+
312+
let mut multi_span: MultiSpan =
313+
spans_and_labels.iter().map(|(sp, _)| *sp).collect::<Vec<_>>().into();
314+
for (sp, label) in spans_and_labels {
315+
multi_span.push_span_label(sp, label);
316+
}
317+
err.span_note(multi_span, "closures can only be coerced to `fn` types if they do not capture any variables");
318+
}
319+
}
320+
}
321+
290322
/// When encountering an `impl Future` where `BoxFuture` is expected, suggest `Box::pin`.
291323
pub(in super::super) fn suggest_calling_boxed_future_when_appropriate(
292324
&self,

src/test/ui/closures/closure-no-fn-1.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
88
|
99
= note: expected fn pointer `fn(u8) -> u8`
1010
found closure `[closure@$DIR/closure-no-fn-1.rs:6:29: 6:50]`
11+
note: closures can only be coerced to `fn` types if they do not capture any variables
12+
--> $DIR/closure-no-fn-1.rs:6:39
13+
|
14+
LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
15+
| ^ `a` captured here
1116

1217
error: aborting due to previous error
1318

src/test/ui/closures/closure-no-fn-2.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ LL | let bar: fn() -> u8 = || { b };
88
|
99
= note: expected fn pointer `fn() -> u8`
1010
found closure `[closure@$DIR/closure-no-fn-2.rs:6:27: 6:35]`
11+
note: closures can only be coerced to `fn` types if they do not capture any variables
12+
--> $DIR/closure-no-fn-2.rs:6:32
13+
|
14+
LL | let bar: fn() -> u8 = || { b };
15+
| ^ `b` captured here
1116

1217
error: aborting due to previous error
1318

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
fn main() {
2+
let b = 2;
3+
let _: fn(usize) -> usize = match true {
4+
true => |a| a + 1,
5+
false => |a| a - b,
6+
//~^ ERROR `match` arms have incompatible types
7+
};
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0308]: `match` arms have incompatible types
2+
--> $DIR/closure-no-fn-4.rs:5:18
3+
|
4+
LL | let _: fn(usize) -> usize = match true {
5+
| _________________________________-
6+
LL | | true => |a| a + 1,
7+
| | --------- this is found to be of type `fn(usize) -> usize`
8+
LL | | false => |a| a - b,
9+
| | ^^^^^^^^^ expected fn pointer, found closure
10+
LL | |
11+
LL | | };
12+
| |_____- `match` arms have incompatible types
13+
|
14+
= note: expected fn pointer `fn(usize) -> usize`
15+
found closure `[closure@$DIR/closure-no-fn-4.rs:5:18: 5:27]`
16+
note: closures can only be coerced to `fn` types if they do not capture any variables
17+
--> $DIR/closure-no-fn-4.rs:5:26
18+
|
19+
LL | false => |a| a - b,
20+
| ^ `b` captured here
21+
22+
error: aborting due to previous error
23+
24+
For more information about this error, try `rustc --explain E0308`.
+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
// When providing diagnostics about not being able to coerce a capturing-closure
2+
// to fn type, we want to report only upto 4 captures.
3+
4+
fn main() {
5+
let a = 0u8;
6+
let b = 0u8;
7+
let c = 0u8;
8+
let d = 0u8;
9+
let e = 0u8;
10+
let bar: fn() -> u8 = || { a; b; c; d; e };
11+
//~^ ERROR mismatched types
12+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/closure-no-fn-5.rs:10:27
3+
|
4+
LL | let bar: fn() -> u8 = || { a; b; c; d; e };
5+
| ---------- ^^^^^^^^^^^^^^^^^^^^ expected fn pointer, found closure
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected fn pointer `fn() -> u8`
10+
found closure `[closure@$DIR/closure-no-fn-5.rs:10:27: 10:47]`
11+
note: closures can only be coerced to `fn` types if they do not capture any variables
12+
--> $DIR/closure-no-fn-5.rs:10:32
13+
|
14+
LL | let bar: fn() -> u8 = || { a; b; c; d; e };
15+
| ^ ^ ^ ^ `d` captured here
16+
| | | |
17+
| | | `c` captured here
18+
| | `b` captured here
19+
| `a` captured here
20+
21+
error: aborting due to previous error
22+
23+
For more information about this error, try `rustc --explain E0308`.

src/test/ui/closures/closure-reform-bad.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ LL | call_bare(f)
88
|
99
= note: expected fn pointer `for<'r> fn(&'r str)`
1010
found closure `[closure@$DIR/closure-reform-bad.rs:10:13: 10:50]`
11+
note: closures can only be coerced to `fn` types if they do not capture any variables
12+
--> $DIR/closure-reform-bad.rs:10:43
13+
|
14+
LL | let f = |s: &str| println!("{}{}", s, string);
15+
| ^^^^^^ `string` captured here
1116

1217
error: aborting due to previous error
1318

src/test/ui/closures/print/closure-print-verbose.stderr

+5
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,11 @@ LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
88
|
99
= note: expected fn pointer `fn(u8) -> u8`
1010
found closure `[main::{closure#0} closure_substs=(unavailable)]`
11+
note: closures can only be coerced to `fn` types if they do not capture any variables
12+
--> $DIR/closure-print-verbose.rs:10:39
13+
|
14+
LL | let foo: fn(u8) -> u8 = |v: u8| { a += v; a };
15+
| ^ `a` captured here
1116

1217
error: aborting due to previous error
1318

0 commit comments

Comments
 (0)