Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
use std::fmt;

use rustc_data_structures::intern::Interned;
use rustc_errors::{Diag, IntoDiagArg};
use rustc_errors::{Applicability, Diag, IntoDiagArg};
use rustc_hir as hir;
use rustc_hir::def::Namespace;
use rustc_hir::def_id::{CRATE_DEF_ID, DefId};
use rustc_middle::bug;
use rustc_middle::ty::error::ExpectedFound;
use rustc_middle::ty::print::{FmtPrinter, Print, PrintTraitRefExt as _, RegionHighlightMode};
use rustc_middle::ty::{self, GenericArgsRef, RePlaceholder, Region, TyCtxt};
use rustc_middle::ty::{self, GenericArgsRef, IsSuggestable, RePlaceholder, Region, TyCtxt};
use tracing::{debug, instrument};

use crate::diagnostics::{
Expand Down Expand Up @@ -379,6 +380,50 @@ impl<'tcx> NiceRegionError<'_, 'tcx> {
}
}

// When the mismatched trait is an Fn-trait and the self type is a closure with
// unannotated parameters, suggest adding explicit type annotations. This turns
// the confusing lifetime-generality error into an actionable hint, e.g.:
// |buf| → |buf: &mut [u8]|
if self.tcx().is_fn_trait(trait_def_id) {
let actual_self_ty = self.cx.resolve_vars_if_possible(
ty::TraitRef::new_from_args(self.cx.tcx, trait_def_id, actual_args).self_ty(),
);
if let ty::Closure(closure_def_id, _) = *actual_self_ty.kind()
&& let Some(local_def_id) = closure_def_id.as_local()
&& let hir::Node::Expr(hir::Expr { kind: hir::ExprKind::Closure(closure), .. }) =
self.tcx().hir_node_by_def_id(local_def_id)
{
let body = self.tcx().hir_body(closure.body);
// For Fn traits, args[1] is the tupled input types (e.g. `(&mut [u8],)`).
let expected_input_tys = expected_args.type_at(1);
if let ty::Tuple(input_tys) = *expected_input_tys.kind() {
let suggestions: Vec<_> = body
.params
.iter()
.zip(input_tys.iter())
.filter_map(|(param, ty)| {
// ty_span == pat.span means no explicit type annotation was written.
if param.ty_span == param.pat.span
&& ty.is_suggestable(self.tcx(), false)
{
Some((param.pat.span.shrink_to_hi(), format!(": {ty}")))
} else {
None
}
})
.collect();
if !suggestions.is_empty() {
err.multipart_suggestion(
"consider adding an explicit type annotation to the closure \
parameter to resolve the lifetime ambiguity",
suggestions,

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

So the thing is that I'm not sure how often this actually fixes the error. This reads a lot like "if you add a type annotation, this will fix the error". We should not overpromise.

Suggested change
suggestions,
"consider adding an explicit type annotation to the closure's argument",

(also, should be argument/arguments depending on if there's more than one suggestion)

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks , changed the suggestion message added a .fixed file too!

Applicability::MaybeIncorrect,
);
}
}
}
}

err
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | require_send(future);
|
= note: closure with signature `fn(&'0 Vec<i32>) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec<i32>,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&Vec<i32>,)>`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | for _ in things.iter().map(|n: &Vec<i32>| n.iter()).flatten() {
| +++++++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/higher-ranked-auto-trait-15.rs:20:5
Expand All @@ -16,6 +20,10 @@ LL | require_send(future);
= note: closure with signature `fn(&'0 Vec<i32>) -> std::slice::Iter<'_, i32>` must implement `FnOnce<(&'1 Vec<i32>,)>`, for any two lifetimes `'0` and `'1`...
= note: ...but it actually implements `FnOnce<(&Vec<i32>,)>`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | for _ in things.iter().map(|n: &Vec<i32>| n.iter()).flatten() {
| +++++++++++

error: aborting due to 2 previous errors

43 changes: 43 additions & 0 deletions tests/ui/closures/hrtb-closure-suggest-type-annotation.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
// When a closure is passed where a higher-ranked `FnOnce` is expected, but the closure
// parameter has no explicit type annotation, the compiler should suggest adding one.
// See <https://github.com/rust-lang/rust/issues/141461>.

fn inner<F>(buf: &mut [u8], func: F) -> usize
where
F: FnOnce(&mut [u8]) -> Result<usize, isize>,
{
match func(buf) {
Ok(n) => n,
Err(e) => e as usize,
}
}

fn outer<F>(buf: &mut [u8], func: F) -> usize
where
F: FnOnce(&mut [u8]) -> Result<usize, isize>,
{
let outer_closure = |buf| {
match func(buf) {
Ok(n) => {
println!("OK: {n}");
Ok(n)
}
Err(e) => {
println!("ERR: {e}");
Err(e)
}
}
};

inner(buf, outer_closure)
//~^ ERROR implementation of `FnOnce` is not general enough
//~| ERROR implementation of `FnOnce` is not general enough
}

fn main() {
let mut x = [0u8; 16];
let res = outer(&mut x, |buf| {
if buf.len() % 2 == 0 { Ok(buf.len()) } else { Err(buf.len() as isize) }
});
println!("{res:?}");
}
29 changes: 29 additions & 0 deletions tests/ui/closures/hrtb-closure-suggest-type-annotation.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
error: implementation of `FnOnce` is not general enough
--> $DIR/hrtb-closure-suggest-type-annotation.rs:32:5
|
LL | inner(buf, outer_closure)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(&'2 mut [u8]) -> Result<usize, isize>` must implement `FnOnce<(&'1 mut [u8],)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 mut [u8],)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let outer_closure = |buf: &mut [u8]| {
| +++++++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/hrtb-closure-suggest-type-annotation.rs:32:5
|
LL | inner(buf, outer_closure)
| ^^^^^^^^^^^^^^^^^^^^^^^^^ implementation of `FnOnce` is not general enough
|
= note: closure with signature `fn(&'2 mut [u8]) -> Result<usize, isize>` must implement `FnOnce<(&'1 mut [u8],)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 mut [u8],)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let outer_closure = |buf: &mut [u8]| {
| +++++++++++

error: aborting due to 2 previous errors

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | foo(bar, "string", |s| s.len() == 5);
|
= note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | foo(bar, "string", |s: &&str| s.len() == 5);
| +++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-71955.rs:45:5
Expand All @@ -16,6 +20,10 @@ LL | foo(bar, "string", |s| s.len() == 5);
= note: closure with signature `for<'a> fn(&'a &'2 str) -> bool` must implement `FnOnce<(&&'1 str,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&&'2 str,)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | foo(bar, "string", |s: &&str| s.len() == 5);
| +++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-71955.rs:48:5
Expand All @@ -25,6 +33,10 @@ LL | foo(baz, "string", |s| s.0.len() == 5);
|
= note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | foo(baz, "string", |s: &Wrapper<'_>| s.0.len() == 5);
| ++++++++++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-71955.rs:48:5
Expand All @@ -35,6 +47,10 @@ LL | foo(baz, "string", |s| s.0.len() == 5);
= note: closure with signature `for<'a> fn(&'a Wrapper<'2>) -> bool` must implement `FnOnce<(&Wrapper<'1>,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&Wrapper<'2>,)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | foo(baz, "string", |s: &Wrapper<'_>| s.0.len() == 5);
| ++++++++++++++

error: aborting due to 4 previous errors

24 changes: 24 additions & 0 deletions tests/ui/lifetimes/issue-105675.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | thing(f);
|
= note: closure with signature `for<'a> fn(&'2 u32, &'a 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`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let f = | _: &u32 , y: &u32 , z: u32 | ();
| ++++++ +++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:5:5
Expand All @@ -16,6 +20,10 @@ LL | thing(f);
= note: closure with signature `for<'a> fn(&'2 u32, &'a 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`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let f = | _: &u32 , y: &u32 , z: u32 | ();
| ++++++ +++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:9:5
Expand All @@ -25,6 +33,10 @@ LL | thing(f);
|
= 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`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let f = | x: &u32, y: _ , z: u32 | ();
| ++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:9:5
Expand All @@ -34,6 +46,10 @@ LL | thing(f);
|
= 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`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let f = | x: &u32, y: _ , z: u32 | ();
| ++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:9:5
Expand All @@ -44,6 +60,10 @@ LL | thing(f);
= 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`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let f = | x: &u32, y: _ , z: u32 | ();
| ++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-105675.rs:9:5
Expand All @@ -54,6 +74,10 @@ LL | thing(f);
= 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`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let f = | x: &u32, y: _ , z: u32 | ();
| ++++++

error: aborting due to 6 previous errors

8 changes: 8 additions & 0 deletions tests/ui/lifetimes/issue-79187-2.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,10 @@ LL | take_foo(|a| a);
|
= note: closure with signature `fn(&'2 i32) -> &i32` must implement `FnOnce<(&'1 i32,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 i32,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | take_foo(|a: &i32| a);
| ++++++

error: implementation of `Fn` is not general enough
--> $DIR/issue-79187-2.rs:8:5
Expand All @@ -33,6 +37,10 @@ LL | take_foo(|a| a);
|
= note: closure with signature `fn(&'2 i32) -> &i32` must implement `Fn<(&'1 i32,)>`, for any lifetime `'1`...
= note: ...but it actually implements `Fn<(&'2 i32,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | take_foo(|a: &i32| a);
| ++++++

error[E0308]: mismatched types
--> $DIR/issue-79187-2.rs:11:5
Expand Down
8 changes: 8 additions & 0 deletions tests/ui/lifetimes/issue-79187.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | thing(f);
|
= note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let f = |_: &u32| ();
| ++++++

error: implementation of `FnOnce` is not general enough
--> $DIR/issue-79187.rs:5:5
Expand All @@ -16,6 +20,10 @@ LL | thing(f);
= note: closure with signature `fn(&'2 u32)` must implement `FnOnce<(&'1 u32,)>`, for any lifetime `'1`...
= note: ...but it actually implements `FnOnce<(&'2 u32,)>`, for some specific lifetime `'2`
= note: duplicate diagnostic emitted due to `-Z deduplicate-diagnostics=no`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | let f = |_: &u32| ();
| ++++++

error: aborting due to 2 previous errors

16 changes: 16 additions & 0 deletions tests/ui/mismatched_types/closure-mismatch.current.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,10 @@ LL | baz(|_| ());
|
= 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`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | baz(|_: &()| ());
| +++++

error: implementation of `Fn` is not general enough
--> $DIR/closure-mismatch.rs:12:5
Expand All @@ -15,6 +19,10 @@ LL | baz(|_| ());
|
= note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`...
= note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | baz(|_: &()| ());
| +++++

error: implementation of `FnOnce` is not general enough
--> $DIR/closure-mismatch.rs:16:5
Expand All @@ -24,6 +32,10 @@ LL | baz(|x| ());
|
= 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`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | baz(|x: &()| ());
| +++++

error: implementation of `Fn` is not general enough
--> $DIR/closure-mismatch.rs:16:5
Expand All @@ -33,6 +45,10 @@ LL | baz(|x| ());
|
= note: closure with signature `fn(&'2 ())` must implement `Fn<(&'1 (),)>`, for any lifetime `'1`...
= note: ...but it actually implements `Fn<(&'2 (),)>`, for some specific lifetime `'2`
help: consider adding an explicit type annotation to the closure parameter to resolve the lifetime ambiguity
|
LL | baz(|x: &()| ());
| +++++

error: aborting due to 4 previous errors

Loading