Skip to content

Commit 1e22280

Browse files
committed
Add suggestions for function pointers
- On compiler-error's suggestion of moving this lower down the stack, along the path of `report_mismatched_types()`, which is used by `rustc_hir_analysis` and `rustc_hir_typeck`. - update ui tests, add test - add suggestions for references to fn pointers - modify `TypeErrCtxt::same_type_modulo_infer` to take `T: relate::Relate` instead of `Ty`
1 parent c8e6a9e commit 1e22280

File tree

14 files changed

+273
-96
lines changed

14 files changed

+273
-96
lines changed

compiler/rustc_hir_typeck/src/coercion.rs

+2
Original file line numberDiff line numberDiff line change
@@ -1613,12 +1613,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
16131613
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
16141614
self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
16151615
}
1616+
16161617
let reported = err.emit_unless(unsized_return);
16171618

16181619
self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported));
16191620
}
16201621
}
16211622
}
1623+
16221624
fn note_unreachable_loop_return(
16231625
&self,
16241626
err: &mut Diagnostic,

compiler/rustc_hir_typeck/src/demand.rs

-1
Original file line numberDiff line numberDiff line change
@@ -81,7 +81,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
8181
self.annotate_expected_due_to_let_ty(err, expr, error);
8282
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
8383
self.note_type_is_not_clone(err, expected, expr_ty, expr);
84-
self.note_need_for_fn_pointer(err, expected, expr_ty);
8584
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
8685
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
8786
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);

compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs

-37
Original file line numberDiff line numberDiff line change
@@ -926,43 +926,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
926926
}
927927
}
928928

929-
pub(in super::super) fn note_need_for_fn_pointer(
930-
&self,
931-
err: &mut Diagnostic,
932-
expected: Ty<'tcx>,
933-
found: Ty<'tcx>,
934-
) {
935-
let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
936-
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
937-
let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
938-
let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
939-
if sig1 != sig2 {
940-
return;
941-
}
942-
err.note(
943-
"different `fn` items always have unique types, even if their signatures are \
944-
the same",
945-
);
946-
(sig1, *did1, substs1)
947-
}
948-
(ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
949-
let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs);
950-
if sig1 != *sig2 {
951-
return;
952-
}
953-
(sig1, *did, substs)
954-
}
955-
_ => return,
956-
};
957-
err.help(&format!("change the expected type to be function pointer `{}`", sig));
958-
err.help(&format!(
959-
"if the expected type is due to type inference, cast the expected `fn` to a function \
960-
pointer: `{} as {}`",
961-
self.tcx.def_path_str_with_substs(did, substs),
962-
sig
963-
));
964-
}
965-
966929
// Instantiates the given path, which must refer to an item with the given
967930
// number of type parameters and type.
968931
#[instrument(skip(self, span), level = "debug")]

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

+2-1
Original file line numberDiff line numberDiff line change
@@ -1841,6 +1841,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
18411841
self.suggest_as_ref_where_appropriate(span, &exp_found, diag);
18421842
self.suggest_accessing_field_where_appropriate(cause, &exp_found, diag);
18431843
self.suggest_await_on_expect_found(cause, span, &exp_found, diag);
1844+
self.suggest_function_pointers(cause, span, &exp_found, diag);
18441845
}
18451846
}
18461847

@@ -2585,7 +2586,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
25852586
/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
25862587
/// FloatVar inference type are compatible with themselves or their concrete types (Int and
25872588
/// Float types, respectively). When comparing two ADTs, these rules apply recursively.
2588-
pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
2589+
pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool {
25892590
let (a, b) = self.resolve_vars_if_possible((a, b));
25902591
SameTypeModuloInfer(self).relate(a, b).is_ok()
25912592
}

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

+77-1
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ use rustc_middle::traits::{
88
StatementAsExpression,
99
};
1010
use rustc_middle::ty::print::with_no_trimmed_paths;
11-
use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
11+
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable};
1212
use rustc_span::{sym, BytePos, Span};
1313

1414
use crate::errors::SuggAddLetForLetChains;
@@ -351,6 +351,82 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
351351
}
352352
}
353353

354+
pub(super) fn suggest_function_pointers(
355+
&self,
356+
cause: &ObligationCause<'tcx>,
357+
span: Span,
358+
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
359+
diag: &mut Diagnostic,
360+
) {
361+
debug!("suggest_function_pointers(cause={:?}, exp_found={:?})", cause, exp_found);
362+
let ty::error::ExpectedFound { expected, found } = exp_found;
363+
let expected_inner = expected.peel_refs();
364+
let found_inner = found.peel_refs();
365+
if !expected_inner.is_fn() || !found_inner.is_fn() {
366+
return;
367+
}
368+
match (&expected_inner.kind(), &found_inner.kind()) {
369+
(ty::FnPtr(sig), ty::FnDef(did, substs)) => {
370+
let expected_sig = &(self.normalize_fn_sig)(*sig);
371+
let found_sig =
372+
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did).subst(self.tcx, substs));
373+
374+
let fn_name = self.tcx.def_path_str_with_substs(*did, substs);
375+
376+
if !self.same_type_modulo_infer(*found_sig, *expected_sig)
377+
|| !sig.is_suggestable(self.tcx, true)
378+
|| ty::util::is_intrinsic(self.tcx, *did)
379+
{
380+
return;
381+
}
382+
383+
let (msg, sugg) = match (expected.is_ref(), found.is_ref()) {
384+
(true, false) => {
385+
let msg = "consider using a reference";
386+
let sug = format!("&{fn_name}");
387+
(msg, sug)
388+
}
389+
(false, true) => {
390+
let msg = "consider removing the reference";
391+
let sug = format!("{fn_name}");
392+
(msg, sug)
393+
}
394+
(true, true) => {
395+
diag.note("fn items are distinct from fn pointers");
396+
let msg = "consider casting to a fn pointer";
397+
let sug = format!("&({fn_name} as {sig})");
398+
(msg, sug)
399+
}
400+
(false, false) => {
401+
diag.note("fn items are distinct from fn pointers");
402+
let msg = "consider casting to a fn pointer";
403+
let sug = format!("{fn_name} as {sig}");
404+
(msg, sug)
405+
}
406+
};
407+
diag.span_suggestion(span, msg, &sugg, Applicability::MaybeIncorrect);
408+
}
409+
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
410+
let expected_sig =
411+
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1));
412+
let found_sig =
413+
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2));
414+
415+
if self.same_type_modulo_infer(*found_sig, *expected_sig) {
416+
diag.note(
417+
"different fn items have unique types, even if their signatures are the same",
418+
);
419+
}
420+
}
421+
(ty::FnDef(_, _), ty::FnPtr(_)) => {
422+
diag.note("fn items are distinct from fn pointers");
423+
}
424+
_ => {
425+
return;
426+
}
427+
};
428+
}
429+
354430
pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
355431
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
356432
(expected.kind(), found.kind())

tests/ui/fn/fn-compare-mismatch.stderr

+1
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ LL | let x = f == g;
1919
|
2020
= note: expected fn item `fn() {f}`
2121
found fn item `fn() {g}`
22+
= note: different fn items have unique types, even if their signatures are the same
2223

2324
error: aborting due to 2 previous errors
2425

tests/ui/fn/fn-item-type.rs

+18-19
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,52 @@
11
// Test that the types of distinct fn items are not compatible by
22
// default. See also `run-pass/fn-item-type-*.rs`.
33

4-
fn foo<T>(x: isize) -> isize { x * 2 }
5-
fn bar<T>(x: isize) -> isize { x * 4 }
4+
fn foo<T>(x: isize) -> isize {
5+
x * 2
6+
}
7+
fn bar<T>(x: isize) -> isize {
8+
x * 4
9+
}
610

7-
fn eq<T>(x: T, y: T) { }
11+
fn eq<T>(x: T, y: T) {}
812

9-
trait Foo { fn foo() { /* this is a default fn */ } }
10-
impl<T> Foo for T { /* `foo` is still default here */ }
13+
trait Foo {
14+
fn foo() { /* this is a default fn */
15+
}
16+
}
17+
impl<T> Foo for T {
18+
/* `foo` is still default here */
19+
}
1120

1221
fn main() {
1322
eq(foo::<u8>, bar::<u8>);
1423
//~^ ERROR mismatched types
1524
//~| expected fn item `fn(_) -> _ {foo::<u8>}`
1625
//~| found fn item `fn(_) -> _ {bar::<u8>}`
1726
//~| expected fn item, found a different fn item
18-
//~| different `fn` items always have unique types, even if their signatures are the same
19-
//~| change the expected type to be function pointer
20-
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
27+
//~| different fn items have unique types, even if their signatures are the same
2128

2229
eq(foo::<u8>, foo::<i8>);
2330
//~^ ERROR mismatched types
2431
//~| expected `u8`, found `i8`
25-
//~| different `fn` items always have unique types, even if their signatures are the same
26-
//~| change the expected type to be function pointer
27-
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
32+
//~| different fn items have unique types, even if their signatures are the same
2833

2934
eq(bar::<String>, bar::<Vec<u8>>);
3035
//~^ ERROR mismatched types
3136
//~| found fn item `fn(_) -> _ {bar::<Vec<u8>>}`
3237
//~| expected struct `String`, found struct `Vec`
33-
//~| different `fn` items always have unique types, even if their signatures are the same
34-
//~| change the expected type to be function pointer
35-
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
38+
//~| different fn items have unique types, even if their signatures are the same
3639

3740
// Make sure we distinguish between trait methods correctly.
3841
eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
3942
//~^ ERROR mismatched types
4043
//~| expected `u8`, found `u16`
41-
//~| different `fn` items always have unique types, even if their signatures are the same
42-
//~| change the expected type to be function pointer
43-
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
44+
//~| different fn items have unique types, even if their signatures are the same
4445

4546
eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
4647
//~^ ERROR mismatched types
4748
//~| found fn pointer `fn(_) -> _`
4849
//~| expected fn item, found fn pointer
49-
//~| change the expected type to be function pointer
50-
//~| if the expected type is due to type inference, cast the expected `fn` to a function pointer
5150

5251
eq(foo::<u8> as fn(isize) -> isize, bar::<u8>); // ok!
5352
}

tests/ui/fn/fn-item-type.stderr

+20-29
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
error[E0308]: mismatched types
2-
--> $DIR/fn-item-type.rs:13:19
2+
--> $DIR/fn-item-type.rs:22:19
33
|
44
LL | eq(foo::<u8>, bar::<u8>);
55
| -- ^^^^^^^^^ expected fn item, found a different fn item
@@ -8,17 +8,15 @@ LL | eq(foo::<u8>, bar::<u8>);
88
|
99
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
1010
found fn item `fn(_) -> _ {bar::<u8>}`
11-
= note: different `fn` items always have unique types, even if their signatures are the same
12-
= help: change the expected type to be function pointer `fn(isize) -> isize`
13-
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
11+
= note: different fn items have unique types, even if their signatures are the same
1412
note: function defined here
15-
--> $DIR/fn-item-type.rs:7:4
13+
--> $DIR/fn-item-type.rs:11:4
1614
|
17-
LL | fn eq<T>(x: T, y: T) { }
15+
LL | fn eq<T>(x: T, y: T) {}
1816
| ^^ ----
1917

2018
error[E0308]: mismatched types
21-
--> $DIR/fn-item-type.rs:22:19
19+
--> $DIR/fn-item-type.rs:29:19
2220
|
2321
LL | eq(foo::<u8>, foo::<i8>);
2422
| -- ^^^^^^^^^ expected `u8`, found `i8`
@@ -27,17 +25,15 @@ LL | eq(foo::<u8>, foo::<i8>);
2725
|
2826
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
2927
found fn item `fn(_) -> _ {foo::<i8>}`
30-
= note: different `fn` items always have unique types, even if their signatures are the same
31-
= help: change the expected type to be function pointer `fn(isize) -> isize`
32-
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
28+
= note: different fn items have unique types, even if their signatures are the same
3329
note: function defined here
34-
--> $DIR/fn-item-type.rs:7:4
30+
--> $DIR/fn-item-type.rs:11:4
3531
|
36-
LL | fn eq<T>(x: T, y: T) { }
32+
LL | fn eq<T>(x: T, y: T) {}
3733
| ^^ ----
3834

3935
error[E0308]: mismatched types
40-
--> $DIR/fn-item-type.rs:29:23
36+
--> $DIR/fn-item-type.rs:34:23
4137
|
4238
LL | eq(bar::<String>, bar::<Vec<u8>>);
4339
| -- ^^^^^^^^^^^^^^ expected struct `String`, found struct `Vec`
@@ -46,17 +42,15 @@ LL | eq(bar::<String>, bar::<Vec<u8>>);
4642
|
4743
= note: expected fn item `fn(_) -> _ {bar::<String>}`
4844
found fn item `fn(_) -> _ {bar::<Vec<u8>>}`
49-
= note: different `fn` items always have unique types, even if their signatures are the same
50-
= help: change the expected type to be function pointer `fn(isize) -> isize`
51-
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `bar::<String> as fn(isize) -> isize`
45+
= note: different fn items have unique types, even if their signatures are the same
5246
note: function defined here
53-
--> $DIR/fn-item-type.rs:7:4
47+
--> $DIR/fn-item-type.rs:11:4
5448
|
55-
LL | fn eq<T>(x: T, y: T) { }
49+
LL | fn eq<T>(x: T, y: T) {}
5650
| ^^ ----
5751

5852
error[E0308]: mismatched types
59-
--> $DIR/fn-item-type.rs:38:26
53+
--> $DIR/fn-item-type.rs:41:26
6054
|
6155
LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
6256
| -- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
@@ -65,17 +59,15 @@ LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
6559
|
6660
= note: expected fn item `fn() {<u8 as Foo>::foo}`
6761
found fn item `fn() {<u16 as Foo>::foo}`
68-
= note: different `fn` items always have unique types, even if their signatures are the same
69-
= help: change the expected type to be function pointer `fn()`
70-
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `<u8 as Foo>::foo as fn()`
62+
= note: different fn items have unique types, even if their signatures are the same
7163
note: function defined here
72-
--> $DIR/fn-item-type.rs:7:4
64+
--> $DIR/fn-item-type.rs:11:4
7365
|
74-
LL | fn eq<T>(x: T, y: T) { }
66+
LL | fn eq<T>(x: T, y: T) {}
7567
| ^^ ----
7668

7769
error[E0308]: mismatched types
78-
--> $DIR/fn-item-type.rs:45:19
70+
--> $DIR/fn-item-type.rs:46:19
7971
|
8072
LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
8173
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
@@ -84,12 +76,11 @@ LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
8476
|
8577
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
8678
found fn pointer `fn(_) -> _`
87-
= help: change the expected type to be function pointer `fn(isize) -> isize`
88-
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
79+
= note: fn items are distinct from fn pointers
8980
note: function defined here
90-
--> $DIR/fn-item-type.rs:7:4
81+
--> $DIR/fn-item-type.rs:11:4
9182
|
92-
LL | fn eq<T>(x: T, y: T) { }
83+
LL | fn eq<T>(x: T, y: T) {}
9384
| ^^ ----
9485

9586
error: aborting due to 5 previous errors

0 commit comments

Comments
 (0)