about summary refs log tree commit diff
diff options
context:
space:
mode:
-rw-r--r--compiler/rustc_trait_selection/messages.ftl2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs2
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs39
-rw-r--r--compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs1
-rw-r--r--compiler/rustc_trait_selection/src/errors.rs6
-rw-r--r--tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs9
-rw-r--r--tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr59
-rw-r--r--tests/ui/typeck/issue-107775.stderr2
8 files changed, 114 insertions, 6 deletions
diff --git a/compiler/rustc_trait_selection/messages.ftl b/compiler/rustc_trait_selection/messages.ftl
index 750d2756b4a..7c72318b4d7 100644
--- a/compiler/rustc_trait_selection/messages.ftl
+++ b/compiler/rustc_trait_selection/messages.ftl
@@ -165,6 +165,8 @@ trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime r
 
 trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}`
 
+trait_selection_fn_consider_casting_both = consider casting both fn items to fn pointers using `as {$sig}`
+
 trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same
 trait_selection_fps_cast = consider casting to a fn pointer
 trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}`
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
index 5f3f65e4696..9eacd377361 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/mod.rs
@@ -1844,7 +1844,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                 self.suggest_tuple_pattern(cause, &exp_found, diag);
                 self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
                 self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
-                self.suggest_function_pointers(cause, span, &exp_found, diag);
+                self.suggest_function_pointers(cause, span, &exp_found, terr, diag);
                 self.suggest_turning_stmt_into_expr(cause, &exp_found, diag);
             }
         }
diff --git a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
index af7e56961b7..231fecf7a4a 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs
@@ -12,6 +12,7 @@ use rustc_middle::traits::{
     IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
     StatementAsExpression,
 };
+use rustc_middle::ty::error::TypeError;
 use rustc_middle::ty::print::with_no_trimmed_paths;
 use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
 use rustc_span::{Span, sym};
@@ -20,7 +21,7 @@ use tracing::debug;
 use crate::error_reporting::TypeErrCtxt;
 use crate::error_reporting::infer::hir::Path;
 use crate::errors::{
-    ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
+    ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes,
     FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
     SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
 };
@@ -381,14 +382,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         }
     }
 
-    pub(super) fn suggest_function_pointers(
+    pub fn suggest_function_pointers_impl(
         &self,
-        cause: &ObligationCause<'tcx>,
-        span: Span,
+        span: Option<Span>,
         exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
         diag: &mut Diag<'_>,
     ) {
-        debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
         let ty::error::ExpectedFound { expected, found } = exp_found;
         let expected_inner = expected.peel_refs();
         let found_inner = found.peel_refs();
@@ -411,6 +410,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                     return;
                 }
 
+                let Some(span) = span else {
+                    let casting = format!("{fn_name} as {sig}");
+                    diag.subdiagnostic(FnItemsAreDistinct);
+                    diag.subdiagnostic(FnConsiderCasting { casting });
+                    return;
+                };
+
                 let sugg = match (expected.is_ref(), found.is_ref()) {
                     (true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
                     (false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
@@ -445,6 +451,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
                 }
 
                 let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
+
+                let Some(span) = span else {
+                    diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig });
+                    return;
+                };
+
                 let sug = if found.is_ref() {
                     FunctionPointerSuggestion::CastBothRef {
                         span,
@@ -488,6 +500,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
         };
     }
 
+    pub(super) fn suggest_function_pointers(
+        &self,
+        cause: &ObligationCause<'tcx>,
+        span: Span,
+        exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
+        terr: TypeError<'tcx>,
+        diag: &mut Diag<'_>,
+    ) {
+        debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
+
+        if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
+            self.suggest_function_pointers_impl(Some(span), exp_found, diag);
+        } else if let TypeError::Sorts(exp_found) = terr {
+            self.suggest_function_pointers_impl(None, &exp_found, diag);
+        }
+    }
+
     pub fn should_suggest_as_ref_kind(
         &self,
         expected: Ty<'tcx>,
diff --git a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
index 5021fd8bf83..6d39cbce3b7 100644
--- a/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
+++ b/compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs
@@ -1969,6 +1969,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
                             StringPart::highlighted(exp_found.found.to_string()),
                             StringPart::normal("`"),
                         ]);
+                        self.suggest_function_pointers_impl(None, &exp_found, err);
                     }
 
                     true
diff --git a/compiler/rustc_trait_selection/src/errors.rs b/compiler/rustc_trait_selection/src/errors.rs
index 2dfa72972ba..c8672b9dbd2 100644
--- a/compiler/rustc_trait_selection/src/errors.rs
+++ b/compiler/rustc_trait_selection/src/errors.rs
@@ -1497,6 +1497,12 @@ pub struct FnConsiderCasting {
 }
 
 #[derive(Subdiagnostic)]
+#[help(trait_selection_fn_consider_casting_both)]
+pub struct FnConsiderCastingBoth<'a> {
+    pub sig: Binder<'a, FnSig<'a>>,
+}
+
+#[derive(Subdiagnostic)]
 pub enum SuggestAccessingField<'a> {
     #[suggestion(
         trait_selection_suggest_accessing_field,
diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs
new file mode 100644
index 00000000000..fa1663d49eb
--- /dev/null
+++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.rs
@@ -0,0 +1,9 @@
+//@ edition: 2021
+
+fn foo() {}
+
+fn main() {
+    let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); //~ ERROR
+    let _: Vec<fn()> = [foo].into_iter().collect(); //~ ERROR
+    let _: Vec<fn()> = Vec::from([foo]); //~ ERROR
+}
diff --git a/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr
new file mode 100644
index 00000000000..d069d39514d
--- /dev/null
+++ b/tests/ui/did_you_mean/casting-fn-item-to-fn-pointer.stderr
@@ -0,0 +1,59 @@
+error[E0277]: a value of type `Vec<(&str, fn())>` cannot be built from an iterator over elements of type `(&str, fn() {foo})`
+  --> $DIR/casting-fn-item-to-fn-pointer.rs:6:59
+   |
+LL |     let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect();
+   |                                                           ^^^^^^^ value of type `Vec<(&str, fn())>` cannot be built from `std::iter::Iterator<Item=(&str, fn() {foo})>`
+   |
+   = help: the trait `FromIterator<(&_, fn() {foo})>` is not implemented for `Vec<(&str, fn())>`
+           but trait `FromIterator<(&_, fn())>` is implemented for it
+   = help: for that trait implementation, expected `fn()`, found `fn() {foo}`
+   = note: fn items are distinct from fn pointers
+   = help: consider casting the fn item to a fn pointer: `foo as fn()`
+note: the method call chain might not have had the expected associated types
+  --> $DIR/casting-fn-item-to-fn-pointer.rs:6:47
+   |
+LL |     let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect();
+   |                                -------------- ^^^^^^^^^^^ `Iterator::Item` is `(&str, fn() {foo})` here
+   |                                |
+   |                                this expression has type `[(&str, fn() {foo}); 1]`
+note: required by a bound in `collect`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+
+error[E0277]: a value of type `Vec<fn()>` cannot be built from an iterator over elements of type `fn() {foo}`
+  --> $DIR/casting-fn-item-to-fn-pointer.rs:7:42
+   |
+LL |     let _: Vec<fn()> = [foo].into_iter().collect();
+   |                                          ^^^^^^^ value of type `Vec<fn()>` cannot be built from `std::iter::Iterator<Item=fn() {foo}>`
+   |
+   = help: the trait `FromIterator<fn() {foo}>` is not implemented for `Vec<fn()>`
+           but trait `FromIterator<fn()>` is implemented for it
+   = help: for that trait implementation, expected `fn()`, found `fn() {foo}`
+   = note: fn items are distinct from fn pointers
+   = help: consider casting the fn item to a fn pointer: `foo as fn()`
+note: the method call chain might not have had the expected associated types
+  --> $DIR/casting-fn-item-to-fn-pointer.rs:7:30
+   |
+LL |     let _: Vec<fn()> = [foo].into_iter().collect();
+   |                        ----- ^^^^^^^^^^^ `Iterator::Item` is `fn() {foo}` here
+   |                        |
+   |                        this expression has type `[fn() {foo}; 1]`
+note: required by a bound in `collect`
+  --> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
+
+error[E0308]: mismatched types
+  --> $DIR/casting-fn-item-to-fn-pointer.rs:8:24
+   |
+LL |     let _: Vec<fn()> = Vec::from([foo]);
+   |            ---------   ^^^^^^^^^^^^^^^^ expected `Vec<fn()>`, found `Vec<fn() {foo}>`
+   |            |
+   |            expected due to this
+   |
+   = note: expected struct `Vec<fn()>`
+              found struct `Vec<fn() {foo}>`
+   = note: fn items are distinct from fn pointers
+   = help: consider casting the fn item to a fn pointer: `foo as fn()`
+
+error: aborting due to 3 previous errors
+
+Some errors have detailed explanations: E0277, E0308.
+For more information about an error, try `rustc --explain E0277`.
diff --git a/tests/ui/typeck/issue-107775.stderr b/tests/ui/typeck/issue-107775.stderr
index 180b0183a3f..dad7e1581e7 100644
--- a/tests/ui/typeck/issue-107775.stderr
+++ b/tests/ui/typeck/issue-107775.stderr
@@ -10,6 +10,8 @@ LL |         Self { map }
    |
    = note: expected struct `HashMap<u16, fn(_) -> Pin<Box<(dyn Future<Output = ()> + Send + 'static)>>>`
               found struct `HashMap<{integer}, fn(_) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>`
+   = note: fn items are distinct from fn pointers
+   = help: consider casting the fn item to a fn pointer: `<Struct as Trait>::do_something::<'_> as fn(u8) -> Pin<Box<(dyn Future<Output = ()> + Send + 'static)>>`
 
 error: aborting due to 1 previous error