Skip to content

Commit 4203627

Browse files
committed
Suggest considering casting fn item as fn pointer in more cases
1 parent f2a3542 commit 4203627

File tree

8 files changed

+114
-6
lines changed

8 files changed

+114
-6
lines changed

compiler/rustc_trait_selection/messages.ftl

+2
Original file line numberDiff line numberDiff line change
@@ -165,6 +165,8 @@ trait_selection_explicit_lifetime_required_with_param_type = explicit lifetime r
165165
166166
trait_selection_fn_consider_casting = consider casting the fn item to a fn pointer: `{$casting}`
167167
168+
trait_selection_fn_consider_casting_both = consider casting both fn items to fn pointers using `as {$sig}`
169+
168170
trait_selection_fn_uniq_types = different fn items have unique types, even if their signatures are the same
169171
trait_selection_fps_cast = consider casting to a fn pointer
170172
trait_selection_fps_cast_both = consider casting both fn items to fn pointers using `as {$expected_sig}`

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

+1-1
Original file line numberDiff line numberDiff line change
@@ -1661,7 +1661,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
16611661
self.suggest_tuple_pattern(cause, &exp_found, diag);
16621662
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
16631663
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
1664-
self.suggest_function_pointers(cause, span, &exp_found, diag);
1664+
self.suggest_function_pointers(cause, span, &exp_found, terr, diag);
16651665
self.suggest_turning_stmt_into_expr(cause, &exp_found, diag);
16661666
}
16671667
}

compiler/rustc_trait_selection/src/error_reporting/infer/suggest.rs

+34-5
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ use rustc_middle::traits::{
1212
IfExpressionCause, MatchExpressionArmCause, ObligationCause, ObligationCauseCode,
1313
StatementAsExpression,
1414
};
15+
use rustc_middle::ty::error::TypeError;
1516
use rustc_middle::ty::print::with_no_trimmed_paths;
1617
use rustc_middle::ty::{self as ty, GenericArgKind, IsSuggestable, Ty, TypeVisitableExt};
1718
use rustc_span::{Span, sym};
@@ -20,7 +21,7 @@ use tracing::debug;
2021
use crate::error_reporting::TypeErrCtxt;
2122
use crate::error_reporting::infer::hir::Path;
2223
use crate::errors::{
23-
ConsiderAddingAwait, FnConsiderCasting, FnItemsAreDistinct, FnUniqTypes,
24+
ConsiderAddingAwait, FnConsiderCasting, FnConsiderCastingBoth, FnItemsAreDistinct, FnUniqTypes,
2425
FunctionPointerSuggestion, SuggestAccessingField, SuggestRemoveSemiOrReturnBinding,
2526
SuggestTuplePatternMany, SuggestTuplePatternOne, TypeErrorAdditionalDiags,
2627
};
@@ -369,14 +370,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
369370
}
370371
}
371372

372-
pub(super) fn suggest_function_pointers(
373+
pub fn suggest_function_pointers_impl(
373374
&self,
374-
cause: &ObligationCause<'tcx>,
375-
span: Span,
375+
span: Option<Span>,
376376
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
377377
diag: &mut Diag<'_>,
378378
) {
379-
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
380379
let ty::error::ExpectedFound { expected, found } = exp_found;
381380
let expected_inner = expected.peel_refs();
382381
let found_inner = found.peel_refs();
@@ -399,6 +398,13 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
399398
return;
400399
}
401400

401+
let Some(span) = span else {
402+
let casting = format!("{fn_name} as {sig}");
403+
diag.subdiagnostic(FnItemsAreDistinct);
404+
diag.subdiagnostic(FnConsiderCasting { casting });
405+
return;
406+
};
407+
402408
let sugg = match (expected.is_ref(), found.is_ref()) {
403409
(true, false) => FunctionPointerSuggestion::UseRef { span, fn_name },
404410
(false, true) => FunctionPointerSuggestion::RemoveRef { span, fn_name },
@@ -433,6 +439,12 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
433439
}
434440

435441
let fn_name = self.tcx.def_path_str_with_args(*did2, args2);
442+
443+
let Some(span) = span else {
444+
diag.subdiagnostic(FnConsiderCastingBoth { sig: *expected_sig });
445+
return;
446+
};
447+
436448
let sug = if found.is_ref() {
437449
FunctionPointerSuggestion::CastBothRef {
438450
span,
@@ -476,6 +488,23 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
476488
};
477489
}
478490

491+
pub(super) fn suggest_function_pointers(
492+
&self,
493+
cause: &ObligationCause<'tcx>,
494+
span: Span,
495+
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
496+
terr: TypeError<'tcx>,
497+
diag: &mut Diag<'_>,
498+
) {
499+
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
500+
501+
if exp_found.expected.peel_refs().is_fn() && exp_found.found.peel_refs().is_fn() {
502+
self.suggest_function_pointers_impl(Some(span), exp_found, diag);
503+
} else if let TypeError::Sorts(exp_found) = terr {
504+
self.suggest_function_pointers_impl(None, &exp_found, diag);
505+
}
506+
}
507+
479508
pub fn should_suggest_as_ref_kind(
480509
&self,
481510
expected: Ty<'tcx>,

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

+1
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
19691969
StringPart::highlighted(exp_found.found.to_string()),
19701970
StringPart::normal("`"),
19711971
]);
1972+
self.suggest_function_pointers_impl(None, &exp_found, err);
19721973
}
19731974

19741975
true

compiler/rustc_trait_selection/src/errors.rs

+6
Original file line numberDiff line numberDiff line change
@@ -1498,6 +1498,12 @@ pub struct FnConsiderCasting {
14981498
pub casting: String,
14991499
}
15001500

1501+
#[derive(Subdiagnostic)]
1502+
#[help(trait_selection_fn_consider_casting_both)]
1503+
pub struct FnConsiderCastingBoth<'a> {
1504+
pub sig: Binder<'a, FnSig<'a>>,
1505+
}
1506+
15011507
#[derive(Subdiagnostic)]
15021508
pub enum SuggestAccessingField<'a> {
15031509
#[suggestion(
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
//@ edition: 2021
2+
3+
fn foo() {}
4+
5+
fn main() {
6+
let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect(); //~ ERROR
7+
let _: Vec<fn()> = [foo].into_iter().collect(); //~ ERROR
8+
let _: Vec<fn()> = Vec::from([foo]); //~ ERROR
9+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
error[E0277]: a value of type `Vec<(&str, fn())>` cannot be built from an iterator over elements of type `(&str, fn() {foo})`
2+
--> $DIR/casting-fn-item-to-fn-pointer.rs:6:59
3+
|
4+
LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect();
5+
| ^^^^^^^ value of type `Vec<(&str, fn())>` cannot be built from `std::iter::Iterator<Item=(&str, fn() {foo})>`
6+
|
7+
= help: the trait `FromIterator<(&_, fn() {foo})>` is not implemented for `Vec<(&str, fn())>`
8+
but trait `FromIterator<(&_, fn())>` is implemented for it
9+
= help: for that trait implementation, expected `fn()`, found `fn() {foo}`
10+
= note: fn items are distinct from fn pointers
11+
= help: consider casting the fn item to a fn pointer: `foo as fn()`
12+
note: the method call chain might not have had the expected associated types
13+
--> $DIR/casting-fn-item-to-fn-pointer.rs:6:47
14+
|
15+
LL | let _: Vec<(&str, fn())> = [("foo", foo)].into_iter().collect();
16+
| -------------- ^^^^^^^^^^^ `Iterator::Item` is `(&str, fn() {foo})` here
17+
| |
18+
| this expression has type `[(&str, fn() {foo}); 1]`
19+
note: required by a bound in `collect`
20+
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
21+
22+
error[E0277]: a value of type `Vec<fn()>` cannot be built from an iterator over elements of type `fn() {foo}`
23+
--> $DIR/casting-fn-item-to-fn-pointer.rs:7:42
24+
|
25+
LL | let _: Vec<fn()> = [foo].into_iter().collect();
26+
| ^^^^^^^ value of type `Vec<fn()>` cannot be built from `std::iter::Iterator<Item=fn() {foo}>`
27+
|
28+
= help: the trait `FromIterator<fn() {foo}>` is not implemented for `Vec<fn()>`
29+
but trait `FromIterator<fn()>` is implemented for it
30+
= help: for that trait implementation, expected `fn()`, found `fn() {foo}`
31+
= note: fn items are distinct from fn pointers
32+
= help: consider casting the fn item to a fn pointer: `foo as fn()`
33+
note: the method call chain might not have had the expected associated types
34+
--> $DIR/casting-fn-item-to-fn-pointer.rs:7:30
35+
|
36+
LL | let _: Vec<fn()> = [foo].into_iter().collect();
37+
| ----- ^^^^^^^^^^^ `Iterator::Item` is `fn() {foo}` here
38+
| |
39+
| this expression has type `[fn() {foo}; 1]`
40+
note: required by a bound in `collect`
41+
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL
42+
43+
error[E0308]: mismatched types
44+
--> $DIR/casting-fn-item-to-fn-pointer.rs:8:24
45+
|
46+
LL | let _: Vec<fn()> = Vec::from([foo]);
47+
| --------- ^^^^^^^^^^^^^^^^ expected `Vec<fn()>`, found `Vec<fn() {foo}>`
48+
| |
49+
| expected due to this
50+
|
51+
= note: expected struct `Vec<fn()>`
52+
found struct `Vec<fn() {foo}>`
53+
= note: fn items are distinct from fn pointers
54+
= help: consider casting the fn item to a fn pointer: `foo as fn()`
55+
56+
error: aborting due to 3 previous errors
57+
58+
Some errors have detailed explanations: E0277, E0308.
59+
For more information about an error, try `rustc --explain E0277`.

tests/ui/typeck/issue-107775.stderr

+2
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,8 @@ LL | Self { map }
1010
|
1111
= note: expected struct `HashMap<u16, fn(_) -> Pin<Box<(dyn Future<Output = ()> + Send + 'static)>>>`
1212
found struct `HashMap<{integer}, fn(_) -> Pin<Box<dyn Future<Output = ()> + Send>> {<Struct as Trait>::do_something::<'_>}>`
13+
= note: fn items are distinct from fn pointers
14+
= 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)>>`
1315

1416
error: aborting due to 1 previous error
1517

0 commit comments

Comments
 (0)