diff options
| author | bors <bors@rust-lang.org> | 2023-04-16 03:06:46 +0000 |
|---|---|---|
| committer | bors <bors@rust-lang.org> | 2023-04-16 03:06:46 +0000 |
| commit | 2a711152615ad9294dc0e5ee6885c8e9bb8418a9 (patch) | |
| tree | 65af8ebd96f84ea2af123065eb3d0faec38db7f4 | |
| parent | c6fb7b9815aea87fb5ced1c683212871699c907c (diff) | |
| parent | 90dc6fe71d6a245b175da6293a41c3156e955526 (diff) | |
| download | rust-2a711152615ad9294dc0e5ee6885c8e9bb8418a9.tar.gz rust-2a711152615ad9294dc0e5ee6885c8e9bb8418a9.zip | |
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...
| -rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/mod.rs | 1 | ||||
| -rw-r--r-- | compiler/rustc_infer/src/infer/error_reporting/suggest.rs | 80 | ||||
| -rw-r--r-- | tests/ui/lifetimes/issue-105675.rs | 14 | ||||
| -rw-r--r-- | tests/ui/lifetimes/issue-105675.stderr | 109 | ||||
| -rw-r--r-- | tests/ui/lifetimes/issue-79187-2.stderr | 4 | ||||
| -rw-r--r-- | tests/ui/lifetimes/issue-79187.stderr | 4 | ||||
| -rw-r--r-- | tests/ui/mismatched_types/closure-mismatch.rs | 3 | ||||
| -rw-r--r-- | tests/ui/mismatched_types/closure-mismatch.stderr | 38 |
8 files changed, 250 insertions, 3 deletions
diff --git a/compiler/rustc_infer/src/infer/error_reporting/mod.rs b/compiler/rustc_infer/src/infer/error_reporting/mod.rs index 9e5f6d107d1..992b07db154 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/mod.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/mod.rs @@ -1927,6 +1927,7 @@ impl<'tcx> TypeErrCtxt<'_, '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 diff --git a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs index b5aeca12a1f..4dc5fc451dd 100644 --- a/compiler/rustc_infer/src/infer/error_reporting/suggest.rs +++ b/compiler/rustc_infer/src/infer/error_reporting/suggest.rs @@ -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 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> { } 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> { diff --git a/tests/ui/lifetimes/issue-105675.rs b/tests/ui/lifetimes/issue-105675.rs new file mode 100644 index 00000000000..58d8be8b65f --- /dev/null +++ b/tests/ui/lifetimes/issue-105675.rs @@ -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 +} diff --git a/tests/ui/lifetimes/issue-105675.stderr b/tests/ui/lifetimes/issue-105675.stderr new file mode 100644 index 00000000000..66415f72bcb --- /dev/null +++ b/tests/ui/lifetimes/issue-105675.stderr @@ -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`. diff --git a/tests/ui/lifetimes/issue-79187-2.stderr b/tests/ui/lifetimes/issue-79187-2.stderr index c5f654b37bf..75fd87b3fe9 100644 --- a/tests/ui/lifetimes/issue-79187-2.stderr +++ b/tests/ui/lifetimes/issue-79187-2.stderr @@ -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 diff --git a/tests/ui/lifetimes/issue-79187.stderr b/tests/ui/lifetimes/issue-79187.stderr index ee6e7b89d5f..209f2b7b739 100644 --- a/tests/ui/lifetimes/issue-79187.stderr +++ b/tests/ui/lifetimes/issue-79187.stderr @@ -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 diff --git a/tests/ui/mismatched_types/closure-mismatch.rs b/tests/ui/mismatched_types/closure-mismatch.rs index b0644e79611..4eb33497c39 100644 --- a/tests/ui/mismatched_types/closure-mismatch.rs +++ b/tests/ui/mismatched_types/closure-mismatch.rs @@ -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 } diff --git a/tests/ui/mismatched_types/closure-mismatch.stderr b/tests/ui/mismatched_types/closure-mismatch.stderr index a7ef8fa0892..c5b8270ba84 100644 --- a/tests/ui/mismatched_types/closure-mismatch.stderr +++ b/tests/ui/mismatched_types/closure-mismatch.stderr @@ -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: 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 2 previous errors +error: aborting due to 4 previous errors For more information about this error, try `rustc --explain E0308`. |
