about summary refs log tree commit diff
diff options
context:
space:
mode:
authorkadmin <julianknodt@gmail.com>2023-01-10 06:08:18 +0000
committerkadmin <julianknodt@gmail.com>2023-01-14 10:37:56 +0000
commit2de9d679adcc2740f490e8f702b2d227be2de30a (patch)
tree610032e2ac9ce46e94ad6565be35d9f7434003a7
parentc0b87359599629d69a7b37318088dac3a5d67c9c (diff)
downloadrust-2de9d679adcc2740f490e8f702b2d227be2de30a.tar.gz
rust-2de9d679adcc2740f490e8f702b2d227be2de30a.zip
Add note when `FnPtr` vs. `FnDef` impl trait
I encountered an instance where an `FnPtr` implemented a trait, but I was passing an `FnDef`. To
the end user, there is really no way to differentiate each of them, 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).
-rw-r--r--compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs54
-rw-r--r--tests/ui/traits/fn-trait-cast-diagnostic.rs26
-rw-r--r--tests/ui/traits/fn-trait-cast-diagnostic.stderr43
-rw-r--r--tests/ui/traits/issue-99875.stderr5
4 files changed, 111 insertions, 17 deletions
diff --git a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
index 0c7ffb056cc..41a64a844ce 100644
--- a/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
+++ b/compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs
@@ -374,6 +374,7 @@ impl<'tcx> InferCtxtExt<'tcx> for InferCtxt<'tcx> {
         })
     }
 }
+
 impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
     fn report_fulfillment_errors(
         &self,
@@ -852,6 +853,29 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
                         let mut suggested =
                             self.suggest_dereferences(&obligation, &mut err, trait_predicate);
                         suggested |= self.suggest_fn_call(&obligation, &mut err, trait_predicate);
+                        let impl_candidates = self.find_similar_impl_candidates(trait_predicate);
+                        suggested = if let &[cand] = &impl_candidates[..] {
+                            let cand = cand.trait_ref;
+                            if let (ty::FnPtr(_), ty::FnDef(..)) =
+                                (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind())
+                            {
+                                err.span_suggestion(
+                                    span.shrink_to_hi(),
+                                    format!(
+                                        "the trait `{}` is implemented for fn pointer `{}`, try casting using `as`",
+                                        cand.print_only_trait_path(),
+                                        cand.self_ty(),
+                                    ),
+                                    format!(" as {}", cand.self_ty()),
+                                    Applicability::MaybeIncorrect,
+                                );
+                                true
+                            } else {
+                                false
+                            }
+                        } else {
+                            false
+                        } || suggested;
                         suggested |=
                             self.suggest_remove_reference(&obligation, &mut err, trait_predicate);
                         suggested |= self.suggest_semicolon_removal(
@@ -1968,27 +1992,25 @@ impl<'tcx> InferCtxtPrivExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
             candidates.sort();
             candidates.dedup();
             let len = candidates.len();
-            if candidates.len() == 0 {
+            if candidates.is_empty() {
                 return false;
             }
-            if candidates.len() == 1 {
-                let ty_desc = match candidates[0].self_ty().kind() {
-                    ty::FnPtr(_) => Some("fn pointer"),
-                    _ => None,
-                };
-                let the_desc = match ty_desc {
-                    Some(desc) => format!(" implemented for {} `", desc),
-                    None => " implemented for `".to_string(),
-                };
+            if let &[cand] = &candidates[..] {
+                let (desc, mention_castable) =
+                    match (cand.self_ty().kind(), trait_ref.self_ty().skip_binder().kind()) {
+                        (ty::FnPtr(_), ty::FnDef(..)) => {
+                            (" implemented for fn pointer `", ", cast using `as`")
+                        }
+                        (ty::FnPtr(_), _) => (" implemented for fn pointer `", ""),
+                        _ => (" implemented for `", ""),
+                    };
                 err.highlighted_help(vec![
-                    (
-                        format!("the trait `{}` ", candidates[0].print_only_trait_path()),
-                        Style::NoStyle,
-                    ),
+                    (format!("the trait `{}` ", cand.print_only_trait_path()), Style::NoStyle),
                     ("is".to_string(), Style::Highlight),
-                    (the_desc, Style::NoStyle),
-                    (candidates[0].self_ty().to_string(), Style::Highlight),
+                    (desc.to_string(), Style::NoStyle),
+                    (cand.self_ty().to_string(), Style::Highlight),
                     ("`".to_string(), Style::NoStyle),
+                    (mention_castable.to_string(), Style::NoStyle),
                 ]);
                 return true;
             }
diff --git a/tests/ui/traits/fn-trait-cast-diagnostic.rs b/tests/ui/traits/fn-trait-cast-diagnostic.rs
new file mode 100644
index 00000000000..e20aa210e58
--- /dev/null
+++ b/tests/ui/traits/fn-trait-cast-diagnostic.rs
@@ -0,0 +1,26 @@
+// There are two different instances to check that even if
+// the trait is implemented for the output of a function,
+// it will still be displayed if the function itself implements a trait.
+trait Foo {}
+
+impl Foo for fn() -> bool {}
+impl Foo for bool {}
+
+fn example() -> bool {
+    true
+}
+
+trait NoOtherFoo {}
+
+impl NoOtherFoo for fn() -> bool {}
+
+fn do_on_foo(v: impl Foo) {}
+fn do_on_single_foo(v: impl NoOtherFoo) {}
+
+fn main() {
+    do_on_foo(example);
+    //~^ ERROR the trait bound
+
+    do_on_single_foo(example);
+    //~^ ERROR the trait bound
+}
diff --git a/tests/ui/traits/fn-trait-cast-diagnostic.stderr b/tests/ui/traits/fn-trait-cast-diagnostic.stderr
new file mode 100644
index 00000000000..6851dcdd504
--- /dev/null
+++ b/tests/ui/traits/fn-trait-cast-diagnostic.stderr
@@ -0,0 +1,43 @@
+error[E0277]: the trait bound `fn() -> bool {example}: Foo` is not satisfied
+  --> $DIR/fn-trait-cast-diagnostic.rs:21:15
+   |
+LL |     do_on_foo(example);
+   |     --------- ^^^^^^^ the trait `Foo` is not implemented for fn item `fn() -> bool {example}`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `do_on_foo`
+  --> $DIR/fn-trait-cast-diagnostic.rs:17:22
+   |
+LL | fn do_on_foo(v: impl Foo) {}
+   |                      ^^^ required by this bound in `do_on_foo`
+help: use parentheses to call this function
+   |
+LL |     do_on_foo(example());
+   |                      ++
+help: the trait `Foo` is implemented for fn pointer `fn() -> bool`, try casting using `as`
+   |
+LL |     do_on_foo(example as fn() -> bool);
+   |                       +++++++++++++++
+
+error[E0277]: the trait bound `fn() -> bool {example}: NoOtherFoo` is not satisfied
+  --> $DIR/fn-trait-cast-diagnostic.rs:24:22
+   |
+LL |     do_on_single_foo(example);
+   |     ---------------- ^^^^^^^ the trait `NoOtherFoo` is not implemented for fn item `fn() -> bool {example}`
+   |     |
+   |     required by a bound introduced by this call
+   |
+note: required by a bound in `do_on_single_foo`
+  --> $DIR/fn-trait-cast-diagnostic.rs:18:29
+   |
+LL | fn do_on_single_foo(v: impl NoOtherFoo) {}
+   |                             ^^^^^^^^^^ required by this bound in `do_on_single_foo`
+help: the trait `NoOtherFoo` is implemented for fn pointer `fn() -> bool`, try casting using `as`
+   |
+LL |     do_on_single_foo(example as fn() -> bool);
+   |                              +++++++++++++++
+
+error: aborting due to 2 previous errors
+
+For more information about this error, try `rustc --explain E0277`.
diff --git a/tests/ui/traits/issue-99875.stderr b/tests/ui/traits/issue-99875.stderr
index 3ff8f12f1b8..fb6eebbd254 100644
--- a/tests/ui/traits/issue-99875.stderr
+++ b/tests/ui/traits/issue-99875.stderr
@@ -6,12 +6,15 @@ LL |     takes(function);
    |     |
    |     required by a bound introduced by this call
    |
-   = help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`
 note: required by a bound in `takes`
   --> $DIR/issue-99875.rs:9:18
    |
 LL | fn takes(_: impl Trait) {}
    |                  ^^^^^ required by this bound in `takes`
+help: the trait `Trait` is implemented for fn pointer `fn(Argument) -> Return`, try casting using `as`
+   |
+LL |     takes(function as fn(Argument) -> Return);
+   |                    +++++++++++++++++++++++++
 
 error[E0277]: the trait bound `[closure@$DIR/issue-99875.rs:14:11: 14:34]: Trait` is not satisfied
   --> $DIR/issue-99875.rs:14:11