Skip to content

Commit 2a71115

Browse files
committed
Auto merge of #105888 - skyzh:skyzh/suggest-lifetime-closure, r=compiler-errors
suggest lifetime for closure parameter type when mismatch This is a draft PR, will add test cases later and be ready for review. This PR fixes #105675 by adding a diagnostics suggestion. Also a partial fix to #105528. The following code will have a compile error now: ``` fn const_if_unit(input: bool) -> impl for<'a> FnOnce(&'a ()) -> usize { let x = |_| 1; x } ``` Before this PR: ``` error[E0308]: mismatched types --> src/lib.rs:3:5 | 3 | x | ^ one type is more general than the other | = note: expected trait `for<'a> FnOnce<(&'a (),)>` found trait `FnOnce<(&(),)>` note: this closure does not fulfill the lifetime requirements --> src/lib.rs:2:13 | 2 | let x = |_| 1; | ^^^ error: implementation of `FnOnce` is not general enough --> src/lib.rs:3:5 | 3 | x | ^ implementation of `FnOnce` is not general enough | = note: closure with signature `fn(&'2 ()) -> usize` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` For more information about this error, try `rustc --explain E0308`. error: could not compile `rust-test` due to 2 previous errors ``` After this PR: ``` error[E0308]: mismatched types --> src/lib.rs:3:5 | 3 | x | ^ one type is more general than the other | = note: expected trait `for<'a> FnOnce<(&'a (),)>` found trait `FnOnce<(&(),)>` note: this closure does not fulfill the lifetime requirements --> src/lib.rs:2:13 | 2 | let x = |_| 1; | ^^^ help: consider changing the type of the closure parameters | 2 | let x = |_: &_| 1; | ~~~~~~~ error: implementation of `FnOnce` is not general enough --> src/lib.rs:3:5 | 3 | x | ^ implementation of `FnOnce` is not general enough | = note: closure with signature `fn(&'2 ()) -> usize` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`... = note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2` For more information about this error, try `rustc --explain E0308`. error: could not compile `rust-test` due to 2 previous errors ``` After applying the suggestion, it compiles. The suggestion might not always be correct as the generation procedure of that suggestion is quite simple...
2 parents c6fb7b9 + 90dc6fe commit 2a71115

File tree

8 files changed

+250
-3
lines changed

8 files changed

+250
-3
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, self.tcx.hir().get_by_def_id(def_id), &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

+78-2
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
use hir::def::CtorKind;
22
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
33
use rustc_data_structures::fx::FxIndexSet;
4-
use rustc_errors::Diagnostic;
4+
use rustc_errors::{Applicability, Diagnostic};
55
use rustc_hir as hir;
66
use rustc_middle::traits::{
77
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
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,82 @@ 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+
hir: hir::Node<'_>,
546+
exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>,
547+
diag: &mut Diagnostic,
548+
) {
549+
// 0. Extract fn_decl from hir
550+
let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }), .. }) = hir else { return; };
551+
let hir::Body { params, .. } = self.tcx.hir().body(*body);
552+
553+
// 1. Get the substs of the closure.
554+
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
555+
let Some(expected) = exp_found.expected.skip_binder().substs.get(1) else { return; };
556+
let Some(found) = exp_found.found.skip_binder().substs.get(1) else { return; };
557+
let expected = expected.unpack();
558+
let found = found.unpack();
559+
// 3. Extract the tuple type from Fn trait and suggest the change.
560+
if let GenericArgKind::Type(expected) = expected &&
561+
let GenericArgKind::Type(found) = found &&
562+
let ty::Tuple(expected) = expected.kind() &&
563+
let ty::Tuple(found)= found.kind() &&
564+
expected.len() == found.len() {
565+
let mut suggestion = "|".to_string();
566+
let mut is_first = true;
567+
let mut has_suggestion = false;
568+
569+
for (((expected, found), param_hir), arg_hir) in expected.iter()
570+
.zip(found.iter())
571+
.zip(params.iter())
572+
.zip(fn_decl.inputs.iter()) {
573+
if is_first {
574+
is_first = false;
575+
} else {
576+
suggestion += ", ";
577+
}
578+
579+
if let ty::Ref(expected_region, _, _) = expected.kind() &&
580+
let ty::Ref(found_region, _, _) = found.kind() &&
581+
expected_region.is_late_bound() &&
582+
!found_region.is_late_bound() &&
583+
let hir::TyKind::Infer = arg_hir.kind {
584+
// If the expected region is late bound, the found region is not, and users are asking compiler
585+
// to infer the type, we can suggest adding `: &_`.
586+
if param_hir.pat.span == param_hir.ty_span {
587+
// for `|x|`, `|_|`, `|x: impl Foo|`
588+
let Ok(pat) = self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) else { return; };
589+
suggestion += &format!("{}: &_", pat);
590+
} else {
591+
// for `|x: ty|`, `|_: ty|`
592+
let Ok(pat) = self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) else { return; };
593+
let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span) else { return; };
594+
suggestion += &format!("{}: &{}", pat, ty);
595+
}
596+
has_suggestion = true;
597+
} else {
598+
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else { return; };
599+
// Otherwise, keep it as-is.
600+
suggestion += &arg;
601+
}
602+
}
603+
suggestion += "|";
604+
605+
if has_suggestion {
606+
diag.span_suggestion_verbose(
607+
span,
608+
"consider specifying the type of the closure parameters",
609+
suggestion,
610+
Applicability::MaybeIncorrect,
611+
);
612+
}
613+
}
614+
}
539615
}
540616

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

tests/ui/lifetimes/issue-105675.rs

+14
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
2+
3+
fn main() {
4+
let f = | _ , y: &u32 , z | ();
5+
thing(f);
6+
//~^ ERROR mismatched types
7+
//~^^ ERROR mismatched types
8+
let f = | x, y: _ , z: u32 | ();
9+
thing(f);
10+
//~^ ERROR mismatched types
11+
//~^^ ERROR mismatched types
12+
//~^^^ ERROR implementation of `FnOnce` is not general enough
13+
//~^^^^ ERROR implementation of `FnOnce` is not general enough
14+
}
+109
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,109 @@
1+
error[E0308]: mismatched types
2+
--> $DIR/issue-105675.rs:5:5
3+
|
4+
LL | thing(f);
5+
| ^^^^^^^^ one type is more general than the other
6+
|
7+
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32, u32)>`
8+
found trait `for<'a> FnOnce<(&u32, &'a u32, u32)>`
9+
note: this closure does not fulfill the lifetime requirements
10+
--> $DIR/issue-105675.rs:4:13
11+
|
12+
LL | let f = | _ , y: &u32 , z | ();
13+
| ^^^^^^^^^^^^^^^^^^^
14+
note: the lifetime requirement is introduced here
15+
--> $DIR/issue-105675.rs:1:18
16+
|
17+
LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
18+
| ^^^^^^^^^^^^^^^^^^^^^^^
19+
help: consider specifying the type of the closure parameters
20+
|
21+
LL | let f = |_: &_, y: &u32, z| ();
22+
| ~~~~~~~~~~~~~~~~~~~
23+
24+
error[E0308]: mismatched types
25+
--> $DIR/issue-105675.rs:5:5
26+
|
27+
LL | thing(f);
28+
| ^^^^^^^^ one type is more general than the other
29+
|
30+
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32, u32)>`
31+
found trait `for<'a> FnOnce<(&u32, &'a u32, u32)>`
32+
note: this closure does not fulfill the lifetime requirements
33+
--> $DIR/issue-105675.rs:4:13
34+
|
35+
LL | let f = | _ , y: &u32 , z | ();
36+
| ^^^^^^^^^^^^^^^^^^^
37+
note: the lifetime requirement is introduced here
38+
--> $DIR/issue-105675.rs:1:18
39+
|
40+
LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
41+
| ^^^^^^^^^^^^^^^^^^^^^^^
42+
43+
error[E0308]: mismatched types
44+
--> $DIR/issue-105675.rs:9:5
45+
|
46+
LL | thing(f);
47+
| ^^^^^^^^ one type is more general than the other
48+
|
49+
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32, u32)>`
50+
found trait `FnOnce<(&u32, &u32, u32)>`
51+
note: this closure does not fulfill the lifetime requirements
52+
--> $DIR/issue-105675.rs:8:13
53+
|
54+
LL | let f = | x, y: _ , z: u32 | ();
55+
| ^^^^^^^^^^^^^^^^^^^^^
56+
note: the lifetime requirement is introduced here
57+
--> $DIR/issue-105675.rs:1:18
58+
|
59+
LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
60+
| ^^^^^^^^^^^^^^^^^^^^^^^
61+
help: consider specifying the type of the closure parameters
62+
|
63+
LL | let f = |x: &_, y: &_, z: u32| ();
64+
| ~~~~~~~~~~~~~~~~~~~~~~
65+
66+
error[E0308]: mismatched types
67+
--> $DIR/issue-105675.rs:9:5
68+
|
69+
LL | thing(f);
70+
| ^^^^^^^^ one type is more general than the other
71+
|
72+
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32, u32)>`
73+
found trait `FnOnce<(&u32, &u32, u32)>`
74+
note: this closure does not fulfill the lifetime requirements
75+
--> $DIR/issue-105675.rs:8:13
76+
|
77+
LL | let f = | x, y: _ , z: u32 | ();
78+
| ^^^^^^^^^^^^^^^^^^^^^
79+
note: the lifetime requirement is introduced here
80+
--> $DIR/issue-105675.rs:1:18
81+
|
82+
LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
83+
| ^^^^^^^^^^^^^^^^^^^^^^^
84+
help: consider specifying the type of the closure parameters
85+
|
86+
LL | let f = |x: &_, y: &_, z: u32| ();
87+
| ~~~~~~~~~~~~~~~~~~~~~~
88+
89+
error: implementation of `FnOnce` is not general enough
90+
--> $DIR/issue-105675.rs:9:5
91+
|
92+
LL | thing(f);
93+
| ^^^^^^^^ implementation of `FnOnce` is not general enough
94+
|
95+
= note: closure with signature `fn(&'2 u32, &u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`...
96+
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2`
97+
98+
error: implementation of `FnOnce` is not general enough
99+
--> $DIR/issue-105675.rs:9:5
100+
|
101+
LL | thing(f);
102+
| ^^^^^^^^ implementation of `FnOnce` is not general enough
103+
|
104+
= note: closure with signature `fn(&u32, &'2 u32, u32)` must implement `FnOnce<(&u32, &'1 u32, u32)>`, for any lifetime `'1`...
105+
= note: ...but it actually implements `FnOnce<(&u32, &'2 u32, u32)>`, for some specific lifetime `'2`
106+
107+
error: aborting due to 6 previous errors
108+
109+
For more information about this error, try `rustc --explain E0308`.

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 specifying the type of the closure parameters
47+
|
48+
LL | take_foo(|a: &_| 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 specifying 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 specifying 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 specifying the type of the closure parameters
61+
|
62+
LL | baz(|x: &_| ());
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)