Skip to content

Commit 7360a6a

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

File tree

6 files changed

+189
-1
lines changed

6 files changed

+189
-1
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/suggest.rs

+130-1
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ use core::ops::ControlFlow;
33
use hir::def::CtorKind;
44
use hir::intravisit::{Visitor, walk_expr, walk_stmt};
55
use hir::{LetStmt, QPath};
6+
use itertools::{EitherOrBoth, Itertools};
67
use rustc_data_structures::fx::FxIndexSet;
78
use rustc_errors::{Applicability, Diag};
89
use rustc_hir as hir;
@@ -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,6 +370,133 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
369370
}
370371
}
371372

373+
fn find_mismatched_fn_item(
374+
&self,
375+
ty1: Ty<'tcx>,
376+
ty2: Ty<'tcx>,
377+
) -> Option<(Ty<'tcx>, Ty<'tcx>)> {
378+
if let Some(fns) = self.find_mismatched_fn_items(ty1, ty2)
379+
&& fns.len() == 1
380+
{
381+
Some(fns[0])
382+
} else {
383+
None
384+
}
385+
}
386+
387+
fn find_mismatched_fn_items(
388+
&self,
389+
ty1: Ty<'tcx>,
390+
ty2: Ty<'tcx>,
391+
) -> Option<Vec<(Ty<'tcx>, Ty<'tcx>)>> {
392+
match (ty1.kind(), ty2.kind()) {
393+
(&ty::Adt(def1, sub1), &ty::Adt(def2, sub2)) if sub1.len() == sub2.len() => {
394+
let did1 = def1.did();
395+
let did2 = def2.did();
396+
397+
if did1 != did2 {
398+
return None;
399+
}
400+
401+
for lifetime in sub1.regions().zip_longest(sub2.regions()) {
402+
match lifetime {
403+
EitherOrBoth::Both(l1, l2) if l1 == l2 => continue,
404+
_ => return None,
405+
}
406+
}
407+
408+
for ca in sub1.consts().zip_longest(sub2.consts()) {
409+
match ca {
410+
EitherOrBoth::Both(c1, c2) if c1 == c2 => continue,
411+
_ => return None,
412+
}
413+
}
414+
415+
let mut fns = Vec::new();
416+
for ty in sub1.types().zip_longest(sub2.types()) {
417+
match ty {
418+
EitherOrBoth::Both(t1, t2) => {
419+
let Some(new_fns) = self.find_mismatched_fn_items(t1, t2) else {
420+
return None;
421+
};
422+
423+
fns.extend(new_fns);
424+
}
425+
_ => return None,
426+
}
427+
}
428+
Some(fns)
429+
}
430+
431+
(&ty::Tuple(args1), &ty::Tuple(args2)) if args1.len() == args2.len() => {
432+
let mut fns = Vec::new();
433+
for (left, right) in args1.iter().zip(args2) {
434+
let Some(new_fns) = self.find_mismatched_fn_items(left, right) else {
435+
return None;
436+
};
437+
fns.extend(new_fns);
438+
}
439+
Some(fns)
440+
}
441+
442+
(ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
443+
let sig1 =
444+
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
445+
let sig2 = &(self.normalize_fn_sig)(sig_tys.with(*hdr));
446+
self.same_type_modulo_infer(*sig1, *sig2).then(|| vec![(ty1, ty2)])
447+
}
448+
449+
(ty::FnDef(did1, args1), ty::FnDef(did2, args2)) => {
450+
let sig1 =
451+
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did1).instantiate(self.tcx, args1));
452+
let sig2 =
453+
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did2).instantiate(self.tcx, args2));
454+
self.same_type_modulo_infer(*sig1, *sig2).then(|| vec![(ty1, ty2)])
455+
}
456+
457+
(ty::FnPtr(sig_tys, hdr), ty::FnDef(did, args)) => {
458+
let sig1 = &(self.normalize_fn_sig)(sig_tys.with(*hdr));
459+
let sig2 =
460+
&(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
461+
self.same_type_modulo_infer(*sig1, *sig2).then(|| vec![(ty1, ty2)])
462+
}
463+
464+
_ => ty1.eq(&ty2).then(|| Vec::new()),
465+
}
466+
}
467+
468+
pub fn suggest_function_pointers_simple(
469+
&self,
470+
diag: &mut Diag<'_>,
471+
found: Ty<'tcx>,
472+
expected: Ty<'tcx>,
473+
) {
474+
let Some((found, expected)) = self.find_mismatched_fn_item(found, expected) else {
475+
return;
476+
};
477+
478+
match (expected.kind(), found.kind()) {
479+
(ty::FnPtr(sig_tys, hdr), ty::FnDef(did, args))
480+
| (ty::FnDef(did, args), ty::FnPtr(sig_tys, hdr)) => {
481+
let sig = sig_tys.with(*hdr);
482+
483+
let fn_name = self.tcx.def_path_str_with_args(*did, args);
484+
let casting = format!("{fn_name} as {sig}");
485+
486+
diag.subdiagnostic(FnItemsAreDistinct);
487+
diag.subdiagnostic(FnConsiderCasting { casting });
488+
}
489+
(ty::FnDef(did, args), ty::FnDef(..)) => {
490+
let sig =
491+
(self.normalize_fn_sig)(self.tcx.fn_sig(*did).instantiate(self.tcx, args));
492+
493+
diag.subdiagnostic(FnUniqTypes);
494+
diag.subdiagnostic(FnConsiderCastingBoth { sig });
495+
}
496+
_ => (),
497+
};
498+
}
499+
372500
pub(super) fn suggest_function_pointers(
373501
&self,
374502
cause: &ObligationCause<'tcx>,
@@ -381,6 +509,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
381509
let expected_inner = expected.peel_refs();
382510
let found_inner = found.peel_refs();
383511
if !expected_inner.is_fn() || !found_inner.is_fn() {
512+
self.suggest_function_pointers_simple(diag, *found, *expected);
384513
return;
385514
}
386515
match (expected_inner.kind(), found_inner.kind()) {

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

+5
Original file line numberDiff line numberDiff line change
@@ -1969,6 +1969,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
19691969
StringPart::highlighted(exp_found.found.to_string()),
19701970
StringPart::normal("`"),
19711971
]);
1972+
self.suggest_function_pointers_simple(
1973+
err,
1974+
exp_found.found,
1975+
exp_found.expected,
1976+
);
19721977
}
19731978

19741979
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,8 @@
1+
//@ edition: 2021
2+
3+
fn foo() {}
4+
5+
fn main() {
6+
let _: Vec<fn()> = [foo].into_iter().collect(); //~ ERROR
7+
let _: Vec<fn()> = Vec::from([foo]); //~ ERROR
8+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
error[E0277]: a value of type `Vec<fn()>` cannot be built from an iterator over elements of type `fn() {foo}`
2+
--> $DIR/casting-fn-item-to-fn-pointer.rs:6:42
3+
|
4+
LL | let _: Vec<fn()> = [foo].into_iter().collect();
5+
| ^^^^^^^ value of type `Vec<fn()>` cannot be built from `std::iter::Iterator<Item=fn() {foo}>`
6+
|
7+
= help: the trait `FromIterator<fn() {foo}>` is not implemented for `Vec<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:30
14+
|
15+
LL | let _: Vec<fn()> = [foo].into_iter().collect();
16+
| ----- ^^^^^^^^^^^ `Iterator::Item` is `fn() {foo}` here
17+
| |
18+
| this expression has type `[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[E0308]: mismatched types
23+
--> $DIR/casting-fn-item-to-fn-pointer.rs:7:24
24+
|
25+
LL | let _: Vec<fn()> = Vec::from([foo]);
26+
| --------- ^^^^^^^^^^^^^^^^ expected `Vec<fn()>`, found `Vec<fn() {foo}>`
27+
| |
28+
| expected due to this
29+
|
30+
= note: expected struct `Vec<fn()>`
31+
found struct `Vec<fn() {foo}>`
32+
= note: fn items are distinct from fn pointers
33+
= help: consider casting the fn item to a fn pointer: `foo as fn()`
34+
35+
error: aborting due to 2 previous errors
36+
37+
Some errors have detailed explanations: E0277, E0308.
38+
For more information about an error, try `rustc --explain E0277`.

0 commit comments

Comments
 (0)