Skip to content

Commit 3fa9be9

Browse files
authored
Rollup merge of rust-lang#106665 - JulianKnodt:better_fn_trait_note, r=cjgillot
Add note when `FnPtr` vs. `FnDef` impl trait I encountered an instance where an `FnPtr` implemented a trait, but I was passing an `FnDef`. I was confused for an hour and to examine the source code of the trait's crate's tests in order to understand how to cast it properly (it didn't help that it was behind a reference). To the end user, it might not be immediately obvious that they are different and how to convert from an `FnDef` to an `FnPtr`, but it is necessary to cast to the generic function in order to compile. It is thus useful to suggest `as` in the help note, (even if the `Fn` output implements the trait).
2 parents bc4049d + 2de9d67 commit 3fa9be9

File tree

4 files changed

+111
-17
lines changed

4 files changed

+111
-17
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+38-16
Original file line numberDiff line numberDiff line change
@@ -374,6 +374,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
374374
})
375375
}
376376
}
377+
377378
impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
378379
fn report_fulfillment_errors(
379380
&self,
@@ -852,6 +853,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
852853
let mut suggested =
853854
self.suggest_dereferences(&obligation, &mut err, trait_predicate);
854855
suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
856+
let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
857+
suggested = if let &[cand] = &impl_candidates[..] {
858+
let cand = cand.trait_ref;
859+
if let (ty::FnPtr(_), ty::FnDef(..)) =
860+
(cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind())
861+
{
862+
err.span_suggestion(
863+
span.shrink_to_hi(),
864+
format!(
865+
"the trait `{}` is implemented for fn pointer `{}`, try casting using `as`",
866+
cand.print_only_trait_path(),
867+
cand.self_ty(),
868+
),
869+
format!(" as {}", cand.self_ty()),
870+
Applicability::MaybeIncorrect,
871+
);
872+
true
873+
} else {
874+
false
875+
}
876+
} else {
877+
false
878+
} || suggested;
855879
suggested |=
856880
self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
857881
suggested |= self.suggest_semicolon_removal(
@@ -1968,27 +1992,25 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
19681992
candidates.sort();
19691993
candidates.dedup();
19701994
let len = candidates.len();
1971-
if candidates.len() == 0 {
1995+
if candidates.is_empty() {
19721996
return false;
19731997
}
1974-
if candidates.len() == 1 {
1975-
let ty_desc = match candidates[0].self_ty().kind() {
1976-
ty::FnPtr(_) => Some("fn pointer"),
1977-
_ => None,
1978-
};
1979-
let the_desc = match ty_desc {
1980-
Some(desc) => format!(" implemented for {} `", desc),
1981-
None => " implemented for `".to_string(),
1982-
};
1998+
if let &[cand] = &candidates[..] {
1999+
let (desc, mention_castable) =
2000+
match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) {
2001+
(ty::FnPtr(_), ty::FnDef(..)) => {
2002+
(" implemented for fn pointer `", ", cast using `as`")
2003+
}
2004+
(ty::FnPtr(_), _) => (" implemented for fn pointer `", ""),
2005+
_ => (" implemented for `", ""),
2006+
};
19832007
err.highlighted_help(vec![
1984-
(
1985-
format!("the trait `{}` ", candidates[0].print_only_trait_path()),
1986-
Style::NoStyle,
1987-
),
2008+
(format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle),
19882009
("is".to_string(), Style::Highlight),
1989-
(the_desc, Style::NoStyle),
1990-
(candidates[0].self_ty().to_string(), Style::Highlight),
2010+
(desc.to_string(), Style::NoStyle),
2011+
(cand.self_ty().to_string(), Style::Highlight),
19912012
("`".to_string(), Style::NoStyle),
2013+
(mention_castable.to_string(), Style::NoStyle),
19922014
]);
19932015
return true;
19942016
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
// There are two different instances to check that even if
2+
// the trait is implemented for the output of a function,
3+
// it will still be displayed if the function itself implements a trait.
4+
trait Foo {}
5+
6+
impl Foo for fn() -> bool {}
7+
impl Foo for bool {}
8+
9+
fn example() -> bool {
10+
true
11+
}
12+
13+
trait NoOtherFoo {}
14+
15+
impl NoOtherFoo for fn() -> bool {}
16+
17+
fn do_on_foo(v: impl Foo) {}
18+
fn do_on_single_foo(v: impl NoOtherFoo) {}
19+
20+
fn main() {
21+
do_on_foo(example);
22+
//~^ ERROR the trait bound
23+
24+
do_on_single_foo(example);
25+
//~^ ERROR the trait bound
26+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
error[E0277]: the trait bound `fn() -> bool {example}: Foo` is not satisfied
2+
--> $DIR/fn-trait-cast-diagnostic.rs:21:15
3+
|
4+
LL | do_on_foo(example);
5+
| --------- ^^^^^^^ the trait `Foo` is not implemented for fn item `fn() -> bool {example}`
6+
| |
7+
| required by a bound introduced by this call
8+
|
9+
note: required by a bound in `do_on_foo`
10+
--> $DIR/fn-trait-cast-diagnostic.rs:17:22
11+
|
12+
LL | fn do_on_foo(v: impl Foo) {}
13+
| ^^^ required by this bound in `do_on_foo`
14+
help: use parentheses to call this function
15+
|
16+
LL | do_on_foo(example());
17+
| ++
18+
help: the trait `Foo` is implemented for fn pointer `fn() -> bool`, try casting using `as`
19+
|
20+
LL | do_on_foo(example as fn() -> bool);
21+
| +++++++++++++++
22+
23+
error[E0277]: the trait bound `fn() -> bool {example}: NoOtherFoo` is not satisfied
24+
--> $DIR/fn-trait-cast-diagnostic.rs:24:22
25+
|
26+
LL | do_on_single_foo(example);
27+
| ---------------- ^^^^^^^ the trait `NoOtherFoo` is not implemented for fn item `fn() -> bool {example}`
28+
| |
29+
| required by a bound introduced by this call
30+
|
31+
note: required by a bound in `do_on_single_foo`
32+
--> $DIR/fn-trait-cast-diagnostic.rs:18:29
33+
|
34+
LL | fn do_on_single_foo(v: impl NoOtherFoo) {}
35+
| ^^^^^^^^^^ required by this bound in `do_on_single_foo`
36+
help: the trait `NoOtherFoo` is implemented for fn pointer `fn() -> bool`, try casting using `as`
37+
|
38+
LL | do_on_single_foo(example as fn() -> bool);
39+
| +++++++++++++++
40+
41+
error: aborting due to 2 previous errors
42+
43+
For more information about this error, try `rustc --explain E0277`.

tests/ui/traits/issue-99875.stderr

+4-1
Original file line numberDiff line numberDiff line change
@@ -6,12 +6,15 @@ LL | takes(function);
66
| |
77
| required by a bound introduced by this call
88
|
9-
= help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`
109
note: required by a bound in `takes`
1110
--> $DIR/issue-99875.rs:9:18
1211
|
1312
LL | fn takes(_: impl Trait) {}
1413
| ^^^^^ required by this bound in `takes`
14+
help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`, try casting using `as`
15+
|
16+
LL | takes(function as fn(Argument) -> Return);
17+
| +++++++++++++++++++++++++
1518

1619
error[E0277]: the trait bound `[closure@$DIR/issue-99875.rs:14:11: 14:34]: Trait` is not satisfied
1720
--> $DIR/issue-99875.rs:14:11

0 commit comments

Comments
 (0)