Skip to content

Commit 6e17349

Browse files
committed
suggest lifetime for closure parameter type when mismatch
1 parent b0884a3 commit 6e17349

File tree

6 files changed

+106
-2
lines changed

6 files changed

+106
-2
lines changed

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1927,6 +1927,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
19271927
{
19281928
let span = self.tcx.def_span(def_id);
19291929
diag.span_note(span, "this closure does not fulfill the lifetime requirements");
1930+
self.suggest_for_all_lifetime_closure(span, &exp_found, diag);
19301931
}
19311932

19321933
// It reads better to have the error origin as the final

compiler/rustc_infer/src/infer/error_reporting/suggest.rs

+57-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_middle::traits::{
88
StatementAsExpression,
99
};
1010
use rustc_middle::ty::print::with_no_trimmed_paths;
11-
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitableExt};
11+
use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
1212
use rustc_span::{sym, BytePos, Span};
1313
use rustc_target::abi::FieldIdx;
1414

@@ -536,6 +536,62 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
536536
}
537537
None
538538
}
539+
540+
/// For "one type is more general than the other" errors on closures, suggest changing the lifetime
541+
/// of the parameters to accept all lifetimes.
542+
pub(super) fn suggest_for_all_lifetime_closure(
543+
&self,
544+
span: Span,
545+
exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>,
546+
diag: &mut Diagnostic,
547+
) {
548+
// 1. Get the substs of the closure.
549+
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
550+
let expected = exp_found.expected.map_bound(|x| x.substs.get(1).cloned()).transpose();
551+
let found = exp_found.found.map_bound(|x| x.substs.get(1).cloned()).transpose();
552+
553+
// 3. Extract the tuple type from Fn trait and suggest the change.
554+
if let (Some(expected), Some(found)) = (expected, found) {
555+
let expected = expected.skip_binder().unpack();
556+
let found = found.skip_binder().unpack();
557+
if let (GenericArgKind::Type(expected), GenericArgKind::Type(found)) = (expected, found)
558+
&& let (ty::Tuple(expected), ty::Tuple(found)) = (expected.kind(), found.kind())
559+
&& expected.len() == found.len() {
560+
let mut suggestion = "|".to_string();
561+
let mut is_first = true;
562+
let mut has_suggestion = false;
563+
564+
for (expected, found) in expected.iter().zip(found.iter()) {
565+
if is_first {
566+
is_first = true;
567+
} else {
568+
suggestion += ", ";
569+
}
570+
571+
if let (ty::Ref(expected_region, _, _), ty::Ref(found_region, _, _)) = (expected.kind(), found.kind())
572+
&& expected_region.is_late_bound() && !found_region.is_late_bound() {
573+
// If the expected region is late bound, and the found region is not, we can suggest adding `: &_`.
574+
// FIXME: use the actual type + variable name provided by user instead of `_`.
575+
suggestion += "_: &_";
576+
has_suggestion = true;
577+
} else {
578+
// Otherwise, keep it as-is.
579+
suggestion += "_";
580+
}
581+
}
582+
suggestion += "|";
583+
584+
if has_suggestion {
585+
diag.span_suggestion_verbose(
586+
span,
587+
"consider changing the type of the closure parameters",
588+
suggestion,
589+
Applicability::MaybeIncorrect,
590+
);
591+
}
592+
}
593+
}
594+
}
539595
}
540596

541597
impl<'tcx> TypeErrCtxt<'_, 'tcx> {

tests/ui/lifetimes/issue-79187-2.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,10 @@ note: the lifetime requirement is introduced here
4343
|
4444
LL | fn take_foo(_: impl Foo) {}
4545
| ^^^
46+
help: consider changing the type of the closure parameters
47+
|
48+
LL | take_foo(|_: &_| a);
49+
| ~~~~~~~
4650

4751
error[E0308]: mismatched types
4852
--> $DIR/issue-79187-2.rs:11:5

tests/ui/lifetimes/issue-79187.stderr

+4
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ note: the lifetime requirement is introduced here
1616
|
1717
LL | fn thing(x: impl FnOnce(&u32)) {}
1818
| ^^^^^^^^^^^^
19+
help: consider changing the type of the closure parameters
20+
|
21+
LL | let f = |_: &_| ();
22+
| ~~~~~~~
1923

2024
error: implementation of `FnOnce` is not general enough
2125
--> $DIR/issue-79187.rs:5:5

tests/ui/mismatched_types/closure-mismatch.rs

+3
Original file line numberDiff line numberDiff line change
@@ -8,4 +8,7 @@ fn main() {
88
baz(|_| ());
99
//~^ ERROR implementation of `FnOnce` is not general enough
1010
//~| ERROR mismatched types
11+
baz(|x| ());
12+
//~^ ERROR implementation of `FnOnce` is not general enough
13+
//~| ERROR mismatched types
1114
}

tests/ui/mismatched_types/closure-mismatch.stderr

+37-1
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,43 @@ note: the lifetime requirement is introduced here
2525
|
2626
LL | fn baz<T: Foo>(_: T) {}
2727
| ^^^
28+
help: consider changing the type of the closure parameters
29+
|
30+
LL | baz(|_: &_| ());
31+
| ~~~~~~~
32+
33+
error: implementation of `FnOnce` is not general enough
34+
--> $DIR/closure-mismatch.rs:11:5
35+
|
36+
LL | baz(|x| ());
37+
| ^^^^^^^^^^^ implementation of `FnOnce` is not general enough
38+
|
39+
= note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`...
40+
= note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2`
41+
42+
error[E0308]: mismatched types
43+
--> $DIR/closure-mismatch.rs:11:5
44+
|
45+
LL | baz(|x| ());
46+
| ^^^^^^^^^^^ one type is more general than the other
47+
|
48+
= note: expected trait `for<'a> Fn<(&'a (),)>`
49+
found trait `Fn<(&(),)>`
50+
note: this closure does not fulfill the lifetime requirements
51+
--> $DIR/closure-mismatch.rs:11:9
52+
|
53+
LL | baz(|x| ());
54+
| ^^^
55+
note: the lifetime requirement is introduced here
56+
--> $DIR/closure-mismatch.rs:5:11
57+
|
58+
LL | fn baz<T: Foo>(_: T) {}
59+
| ^^^
60+
help: consider changing the type of the closure parameters
61+
|
62+
LL | baz(|_: &_| ());
63+
| ~~~~~~~
2864

29-
error: aborting due to 2 previous errors
65+
error: aborting due to 4 previous errors
3066

3167
For more information about this error, try `rustc --explain E0308`.

0 commit comments

Comments
 (0)