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 https://github.com/rust-lang/rust/issues/105675 by adding a diagnostics suggestion. Also a partial fix to https://github.com/rust-lang/rust/issues/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...
This commit is contained in:
commit
2a71115261
@ -1927,6 +1927,7 @@ enum Similar<'tcx> {
|
||||
{
|
||||
let span = self.tcx.def_span(def_id);
|
||||
diag.span_note(span, "this closure does not fulfill the lifetime requirements");
|
||||
self.suggest_for_all_lifetime_closure(span, self.tcx.hir().get_by_def_id(def_id), &exp_found, diag);
|
||||
}
|
||||
|
||||
// It reads better to have the error origin as the final
|
||||
|
@ -1,14 +1,14 @@
|
||||
use hir::def::CtorKind;
|
||||
use hir::intravisit::{walk_expr, walk_stmt, Visitor};
|
||||
use rustc_data_structures::fx::FxIndexSet;
|
||||
use rustc_errors::Diagnostic;
|
||||
use rustc_errors::{Applicability, Diagnostic};
|
||||
use rustc_hir as hir;
|
||||
use rustc_middle::traits::{
|
||||
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
|
||||
StatementAsExpression,
|
||||
};
|
||||
use rustc_middle::ty::print::with_no_trimmed_paths;
|
||||
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitableExt};
|
||||
use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
|
||||
use rustc_span::{sym, BytePos, Span};
|
||||
use rustc_target::abi::FieldIdx;
|
||||
|
||||
@ -536,6 +536,82 @@ fn visit_body(&mut self, body: &'v hir::Body<'v>) {
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
/// For "one type is more general than the other" errors on closures, suggest changing the lifetime
|
||||
/// of the parameters to accept all lifetimes.
|
||||
pub(super) fn suggest_for_all_lifetime_closure(
|
||||
&self,
|
||||
span: Span,
|
||||
hir: hir::Node<'_>,
|
||||
exp_found: &ty::error::ExpectedFound<ty::PolyTraitRef<'tcx>>,
|
||||
diag: &mut Diagnostic,
|
||||
) {
|
||||
// 0. Extract fn_decl from hir
|
||||
let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(hir::Closure { body, fn_decl, .. }), .. }) = hir else { return; };
|
||||
let hir::Body { params, .. } = self.tcx.hir().body(*body);
|
||||
|
||||
// 1. Get the substs of the closure.
|
||||
// 2. Assume exp_found is FnOnce / FnMut / Fn, we can extract function parameters from [1].
|
||||
let Some(expected) = exp_found.expected.skip_binder().substs.get(1) else { return; };
|
||||
let Some(found) = exp_found.found.skip_binder().substs.get(1) else { return; };
|
||||
let expected = expected.unpack();
|
||||
let found = found.unpack();
|
||||
// 3. Extract the tuple type from Fn trait and suggest the change.
|
||||
if let GenericArgKind::Type(expected) = expected &&
|
||||
let GenericArgKind::Type(found) = found &&
|
||||
let ty::Tuple(expected) = expected.kind() &&
|
||||
let ty::Tuple(found)= found.kind() &&
|
||||
expected.len() == found.len() {
|
||||
let mut suggestion = "|".to_string();
|
||||
let mut is_first = true;
|
||||
let mut has_suggestion = false;
|
||||
|
||||
for (((expected, found), param_hir), arg_hir) in expected.iter()
|
||||
.zip(found.iter())
|
||||
.zip(params.iter())
|
||||
.zip(fn_decl.inputs.iter()) {
|
||||
if is_first {
|
||||
is_first = false;
|
||||
} else {
|
||||
suggestion += ", ";
|
||||
}
|
||||
|
||||
if let ty::Ref(expected_region, _, _) = expected.kind() &&
|
||||
let ty::Ref(found_region, _, _) = found.kind() &&
|
||||
expected_region.is_late_bound() &&
|
||||
!found_region.is_late_bound() &&
|
||||
let hir::TyKind::Infer = arg_hir.kind {
|
||||
// If the expected region is late bound, the found region is not, and users are asking compiler
|
||||
// to infer the type, we can suggest adding `: &_`.
|
||||
if param_hir.pat.span == param_hir.ty_span {
|
||||
// for `|x|`, `|_|`, `|x: impl Foo|`
|
||||
let Ok(pat) = self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) else { return; };
|
||||
suggestion += &format!("{}: &_", pat);
|
||||
} else {
|
||||
// for `|x: ty|`, `|_: ty|`
|
||||
let Ok(pat) = self.tcx.sess.source_map().span_to_snippet(param_hir.pat.span) else { return; };
|
||||
let Ok(ty) = self.tcx.sess.source_map().span_to_snippet(param_hir.ty_span) else { return; };
|
||||
suggestion += &format!("{}: &{}", pat, ty);
|
||||
}
|
||||
has_suggestion = true;
|
||||
} else {
|
||||
let Ok(arg) = self.tcx.sess.source_map().span_to_snippet(param_hir.span) else { return; };
|
||||
// Otherwise, keep it as-is.
|
||||
suggestion += &arg;
|
||||
}
|
||||
}
|
||||
suggestion += "|";
|
||||
|
||||
if has_suggestion {
|
||||
diag.span_suggestion_verbose(
|
||||
span,
|
||||
"consider specifying the type of the closure parameters",
|
||||
suggestion,
|
||||
Applicability::MaybeIncorrect,
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl<'tcx> TypeErrCtxt<'_, 'tcx> {
|
||||
|
14
tests/ui/lifetimes/issue-105675.rs
Normal file
14
tests/ui/lifetimes/issue-105675.rs
Normal file
@ -0,0 +1,14 @@
|
||||
fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
|
||||
|
||||
fn main() {
|
||||
let f = | _ , y: &u32 , z | ();
|
||||
thing(f);
|
||||
//~^ ERROR mismatched types
|
||||
//~^^ ERROR mismatched types
|
||||
let f = | x, y: _ , z: u32 | ();
|
||||
thing(f);
|
||||
//~^ ERROR mismatched types
|
||||
//~^^ ERROR mismatched types
|
||||
//~^^^ ERROR implementation of `FnOnce` is not general enough
|
||||
//~^^^^ ERROR implementation of `FnOnce` is not general enough
|
||||
}
|
109
tests/ui/lifetimes/issue-105675.stderr
Normal file
109
tests/ui/lifetimes/issue-105675.stderr
Normal file
@ -0,0 +1,109 @@
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-105675.rs:5:5
|
||||
|
|
||||
LL | thing(f);
|
||||
| ^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32, u32)>`
|
||||
found trait `for<'a> FnOnce<(&u32, &'a u32, u32)>`
|
||||
note: this closure does not fulfill the lifetime requirements
|
||||
--> $DIR/issue-105675.rs:4:13
|
||||
|
|
||||
LL | let f = | _ , y: &u32 , z | ();
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
note: the lifetime requirement is introduced here
|
||||
--> $DIR/issue-105675.rs:1:18
|
||||
|
|
||||
LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: consider specifying the type of the closure parameters
|
||||
|
|
||||
LL | let f = |_: &_, y: &u32, z| ();
|
||||
| ~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-105675.rs:5:5
|
||||
|
|
||||
LL | thing(f);
|
||||
| ^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32, u32)>`
|
||||
found trait `for<'a> FnOnce<(&u32, &'a u32, u32)>`
|
||||
note: this closure does not fulfill the lifetime requirements
|
||||
--> $DIR/issue-105675.rs:4:13
|
||||
|
|
||||
LL | let f = | _ , y: &u32 , z | ();
|
||||
| ^^^^^^^^^^^^^^^^^^^
|
||||
note: the lifetime requirement is introduced here
|
||||
--> $DIR/issue-105675.rs:1:18
|
||||
|
|
||||
LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-105675.rs:9:5
|
||||
|
|
||||
LL | thing(f);
|
||||
| ^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32, u32)>`
|
||||
found trait `FnOnce<(&u32, &u32, u32)>`
|
||||
note: this closure does not fulfill the lifetime requirements
|
||||
--> $DIR/issue-105675.rs:8:13
|
||||
|
|
||||
LL | let f = | x, y: _ , z: u32 | ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
note: the lifetime requirement is introduced here
|
||||
--> $DIR/issue-105675.rs:1:18
|
||||
|
|
||||
LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: consider specifying the type of the closure parameters
|
||||
|
|
||||
LL | let f = |x: &_, y: &_, z: u32| ();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-105675.rs:9:5
|
||||
|
|
||||
LL | thing(f);
|
||||
| ^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected trait `for<'a, 'b> FnOnce<(&'a u32, &'b u32, u32)>`
|
||||
found trait `FnOnce<(&u32, &u32, u32)>`
|
||||
note: this closure does not fulfill the lifetime requirements
|
||||
--> $DIR/issue-105675.rs:8:13
|
||||
|
|
||||
LL | let f = | x, y: _ , z: u32 | ();
|
||||
| ^^^^^^^^^^^^^^^^^^^^^
|
||||
note: the lifetime requirement is introduced here
|
||||
--> $DIR/issue-105675.rs:1:18
|
||||
|
|
||||
LL | fn thing(x: impl FnOnce(&u32, &u32, u32)) {}
|
||||
| ^^^^^^^^^^^^^^^^^^^^^^^
|
||||
help: consider specifying the type of the closure parameters
|
||||
|
|
||||
LL | let f = |x: &_, y: &_, z: u32| ();
|
||||
| ~~~~~~~~~~~~~~~~~~~~~~
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/issue-105675.rs:9:5
|
||||
|
|
||||
LL | thing(f);
|
||||
| ^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 u32, &u32, u32)` must implement `FnOnce<(&'1 u32, &u32, u32)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 u32, &u32, u32)>`, for some specific lifetime `'2`
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/issue-105675.rs:9:5
|
||||
|
|
||||
LL | thing(f);
|
||||
| ^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&u32, &'2 u32, u32)` must implement `FnOnce<(&u32, &'1 u32, u32)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&u32, &'2 u32, u32)>`, for some specific lifetime `'2`
|
||||
|
||||
error: aborting due to 6 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
@ -43,6 +43,10 @@ note: the lifetime requirement is introduced here
|
||||
|
|
||||
LL | fn take_foo(_: impl Foo) {}
|
||||
| ^^^
|
||||
help: consider specifying the type of the closure parameters
|
||||
|
|
||||
LL | take_foo(|a: &_| a);
|
||||
| ~~~~~~~
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/issue-79187-2.rs:11:5
|
||||
|
@ -16,6 +16,10 @@ note: the lifetime requirement is introduced here
|
||||
|
|
||||
LL | fn thing(x: impl FnOnce(&u32)) {}
|
||||
| ^^^^^^^^^^^^
|
||||
help: consider specifying the type of the closure parameters
|
||||
|
|
||||
LL | let f = |_: &_| ();
|
||||
| ~~~~~~~
|
||||
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/issue-79187.rs:5:5
|
||||
|
@ -8,4 +8,7 @@ fn main() {
|
||||
baz(|_| ());
|
||||
//~^ ERROR implementation of `FnOnce` is not general enough
|
||||
//~| ERROR mismatched types
|
||||
baz(|x| ());
|
||||
//~^ ERROR implementation of `FnOnce` is not general enough
|
||||
//~| ERROR mismatched types
|
||||
}
|
||||
|
@ -25,7 +25,43 @@ note: the lifetime requirement is introduced here
|
||||
|
|
||||
LL | fn baz<T: Foo>(_: T) {}
|
||||
| ^^^
|
||||
help: consider specifying the type of the closure parameters
|
||||
|
|
||||
LL | baz(|_: &_| ());
|
||||
| ~~~~~~~
|
||||
|
||||
error: aborting due to 2 previous errors
|
||||
error: implementation of `FnOnce` is not general enough
|
||||
--> $DIR/closure-mismatch.rs:11:5
|
||||
|
|
||||
LL | baz(|x| ());
|
||||
| ^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
||||
|
|
||||
= note: closure with signature `fn(&'2 ())` must implement `FnOnce<(&'1 (),)>`, for any lifetime `'1`...
|
||||
= note: ...but it actually implements `FnOnce<(&'2 (),)>`, for some specific lifetime `'2`
|
||||
|
||||
error[E0308]: mismatched types
|
||||
--> $DIR/closure-mismatch.rs:11:5
|
||||
|
|
||||
LL | baz(|x| ());
|
||||
| ^^^^^^^^^^^ one type is more general than the other
|
||||
|
|
||||
= note: expected trait `for<'a> Fn<(&'a (),)>`
|
||||
found trait `Fn<(&(),)>`
|
||||
note: this closure does not fulfill the lifetime requirements
|
||||
--> $DIR/closure-mismatch.rs:11:9
|
||||
|
|
||||
LL | baz(|x| ());
|
||||
| ^^^
|
||||
note: the lifetime requirement is introduced here
|
||||
--> $DIR/closure-mismatch.rs:5:11
|
||||
|
|
||||
LL | fn baz<T: Foo>(_: T) {}
|
||||
| ^^^
|
||||
help: consider specifying the type of the closure parameters
|
||||
|
|
||||
LL | baz(|x: &_| ());
|
||||
| ~~~~~~~
|
||||
|
||||
error: aborting due to 4 previous errors
|
||||
|
||||
For more information about this error, try `rustc --explain E0308`.
|
||||
|
Loading…
Reference in New Issue
Block a user