Skip to content

Add help message about function pointers #105552

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 1 commit into from
Jan 25, 2023
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions compiler/rustc_hir_typeck/src/coercion.rs
Original file line number Diff line number Diff line change
@@ -1613,12 +1613,14 @@ impl<'tcx, 'exprs, E: AsCoercionSite> CoerceMany<'tcx, 'exprs, E> {
if visitor.ret_exprs.len() > 0 && let Some(expr) = expression {
self.note_unreachable_loop_return(&mut err, &expr, &visitor.ret_exprs);
}

let reported = err.emit_unless(unsized_return);

self.final_ty = Some(fcx.tcx.ty_error_with_guaranteed(reported));
}
}
}

fn note_unreachable_loop_return(
&self,
err: &mut Diagnostic,
1 change: 0 additions & 1 deletion compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
@@ -81,7 +81,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
self.annotate_expected_due_to_let_ty(err, expr, error);
self.emit_type_mismatch_suggestions(err, expr, expr_ty, expected, expected_ty_expr, error);
self.note_type_is_not_clone(err, expected, expr_ty, expr);
self.note_need_for_fn_pointer(err, expected, expr_ty);
self.note_internal_mutation_in_method(err, expr, expected, expr_ty);
self.check_for_range_as_method_call(err, expr, expr_ty, expected);
self.check_for_binding_assigned_block_without_tail_expression(err, expr, expr_ty, expected);
37 changes: 0 additions & 37 deletions compiler/rustc_hir_typeck/src/fn_ctxt/_impl.rs
Original file line number Diff line number Diff line change
@@ -926,43 +926,6 @@ impl<'a, 'tcx> FnCtxt<'a, 'tcx> {
}
}

pub(in super::super) fn note_need_for_fn_pointer(
&self,
err: &mut Diagnostic,
expected: Ty<'tcx>,
found: Ty<'tcx>,
) {
let (sig, did, substs) = match (&expected.kind(), &found.kind()) {
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
let sig1 = self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1);
let sig2 = self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2);
if sig1 != sig2 {
return;
}
err.note(
"different `fn` items always have unique types, even if their signatures are \
the same",
);
(sig1, *did1, substs1)
}
(ty::FnDef(did, substs), ty::FnPtr(sig2)) => {
let sig1 = self.tcx.bound_fn_sig(*did).subst(self.tcx, substs);
if sig1 != *sig2 {
return;
}
(sig1, *did, substs)
}
_ => return,
};
err.help(&format!("change the expected type to be function pointer `{}`", sig));
err.help(&format!(
"if the expected type is due to type inference, cast the expected `fn` to a function \
pointer: `{} as {}`",
self.tcx.def_path_str_with_substs(did, substs),
sig
));
}

// Instantiates the given path, which must refer to an item with the given
// number of type parameters and type.
#[instrument(skip(self, span), level = "debug")]
3 changes: 2 additions & 1 deletion compiler/rustc_infer/src/infer/error_reporting/mod.rs
Original file line number Diff line number Diff line change
@@ -1841,6 +1841,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
self.suggest_as_ref_where_appropriate(span, &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);
}
}

@@ -2585,7 +2586,7 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
/// with the other type. A TyVar inference type is compatible with any type, and an IntVar or
/// FloatVar inference type are compatible with themselves or their concrete types (Int and
/// Float types, respectively). When comparing two ADTs, these rules apply recursively.
pub fn same_type_modulo_infer(&self, a: Ty<'tcx>, b: Ty<'tcx>) -> bool {
pub fn same_type_modulo_infer<T: relate::Relate<'tcx>>(&self, a: T, b: T) -> bool {
let (a, b) = self.resolve_vars_if_possible((a, b));
SameTypeModuloInfer(self).relate(a, b).is_ok()
}
78 changes: 77 additions & 1 deletion compiler/rustc_infer/src/infer/error_reporting/suggest.rs
Original file line number Diff line number Diff line change
@@ -8,7 +8,7 @@ use rustc_middle::traits::{
StatementAsExpression,
};
use rustc_middle::ty::print::with_no_trimmed_paths;
use rustc_middle::ty::{self as ty, Ty, TypeVisitable};
use rustc_middle::ty::{self as ty, IsSuggestable, Ty, TypeVisitable};
use rustc_span::{sym, BytePos, Span};

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

pub(super) fn suggest_function_pointers(
&self,
cause: &ObligationCause<'tcx>,
span: Span,
exp_found: &ty::error::ExpectedFound<Ty<'tcx>>,
diag: &mut Diagnostic,
) {
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();
if !expected_inner.is_fn() || !found_inner.is_fn() {
return;
}
match (&expected_inner.kind(), &found_inner.kind()) {
(ty::FnPtr(sig), ty::FnDef(did, substs)) => {
let expected_sig = &(self.normalize_fn_sig)(*sig);
let found_sig =
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did).subst(self.tcx, substs));

let fn_name = self.tcx.def_path_str_with_substs(*did, substs);

if !self.same_type_modulo_infer(*found_sig, *expected_sig)
|| !sig.is_suggestable(self.tcx, true)
|| ty::util::is_intrinsic(self.tcx, *did)
{
return;
}

let (msg, sugg) = match (expected.is_ref(), found.is_ref()) {
(true, false) => {
let msg = "consider using a reference";
let sug = format!("&{fn_name}");
(msg, sug)
}
(false, true) => {
let msg = "consider removing the reference";
let sug = format!("{fn_name}");
(msg, sug)
}
(true, true) => {
diag.note("fn items are distinct from fn pointers");
let msg = "consider casting to a fn pointer";
let sug = format!("&({fn_name} as {sig})");
(msg, sug)
}
(false, false) => {
diag.note("fn items are distinct from fn pointers");
let msg = "consider casting to a fn pointer";
let sug = format!("{fn_name} as {sig}");
(msg, sug)
}
};
diag.span_suggestion(span, msg, &sugg, Applicability::MaybeIncorrect);
}
(ty::FnDef(did1, substs1), ty::FnDef(did2, substs2)) => {
let expected_sig =
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did1).subst(self.tcx, substs1));
let found_sig =
&(self.normalize_fn_sig)(self.tcx.bound_fn_sig(*did2).subst(self.tcx, substs2));

if self.same_type_modulo_infer(*found_sig, *expected_sig) {
diag.note(
"different fn items have unique types, even if their signatures are the same",
);
}
}
(ty::FnDef(_, _), ty::FnPtr(_)) => {
diag.note("fn items are distinct from fn pointers");
}
_ => {
return;
}
};
}

pub fn should_suggest_as_ref(&self, expected: Ty<'tcx>, found: Ty<'tcx>) -> Option<&str> {
if let (ty::Adt(exp_def, exp_substs), ty::Ref(_, found_ty, _)) =
(expected.kind(), found.kind())
1 change: 1 addition & 0 deletions tests/ui/fn/fn-compare-mismatch.stderr
Original file line number Diff line number Diff line change
@@ -19,6 +19,7 @@ LL | let x = f == g;
|
= note: expected fn item `fn() {f}`
found fn item `fn() {g}`
= note: different fn items have unique types, even if their signatures are the same

error: aborting due to 2 previous errors

37 changes: 18 additions & 19 deletions tests/ui/fn/fn-item-type.rs
Original file line number Diff line number Diff line change
@@ -1,53 +1,52 @@
// Test that the types of distinct fn items are not compatible by
// default. See also `run-pass/fn-item-type-*.rs`.

fn foo<T>(x: isize) -> isize { x * 2 }
fn bar<T>(x: isize) -> isize { x * 4 }
fn foo<T>(x: isize) -> isize {
x * 2
}
fn bar<T>(x: isize) -> isize {
x * 4
}

fn eq<T>(x: T, y: T) { }
fn eq<T>(x: T, y: T) {}

trait Foo { fn foo() { /* this is a default fn */ } }
impl<T> Foo for T { /* `foo` is still default here */ }
trait Foo {
fn foo() { /* this is a default fn */
}
}
impl<T> Foo for T {
/* `foo` is still default here */
}

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

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

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

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

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

eq(foo::<u8> as fn(isize) -> isize, bar::<u8>); // ok!
}
49 changes: 20 additions & 29 deletions tests/ui/fn/fn-item-type.stderr
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
error[E0308]: mismatched types
--> $DIR/fn-item-type.rs:13:19
--> $DIR/fn-item-type.rs:22:19
|
LL | eq(foo::<u8>, bar::<u8>);
| -- ^^^^^^^^^ expected fn item, found a different fn item
@@ -8,17 +8,15 @@ LL | eq(foo::<u8>, bar::<u8>);
|
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
found fn item `fn(_) -> _ {bar::<u8>}`
= note: different `fn` items always have unique types, even if their signatures are the same
= help: change the expected type to be function pointer `fn(isize) -> isize`
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. When we have two FnDefs, we should mention that the user can use an as to turn them into pointers.

= note: different fn items have unique types, even if their signatures are the same
note: function defined here
--> $DIR/fn-item-type.rs:7:4
--> $DIR/fn-item-type.rs:11:4
|
LL | fn eq<T>(x: T, y: T) { }
LL | fn eq<T>(x: T, y: T) {}
| ^^ ----

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

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

error[E0308]: mismatched types
--> $DIR/fn-item-type.rs:38:26
--> $DIR/fn-item-type.rs:41:26
|
LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
| -- ^^^^^^^^^^^^^^^^^ expected `u8`, found `u16`
@@ -65,17 +59,15 @@ LL | eq(<u8 as Foo>::foo, <u16 as Foo>::foo);
|
= note: expected fn item `fn() {<u8 as Foo>::foo}`
found fn item `fn() {<u16 as Foo>::foo}`
= note: different `fn` items always have unique types, even if their signatures are the same
= help: change the expected type to be function pointer `fn()`
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `<u8 as Foo>::foo as fn()`
= note: different fn items have unique types, even if their signatures are the same
note: function defined here
--> $DIR/fn-item-type.rs:7:4
--> $DIR/fn-item-type.rs:11:4
|
LL | fn eq<T>(x: T, y: T) { }
LL | fn eq<T>(x: T, y: T) {}
| ^^ ----

error[E0308]: mismatched types
--> $DIR/fn-item-type.rs:45:19
--> $DIR/fn-item-type.rs:46:19
|
LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
| -- ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected fn item, found fn pointer
@@ -84,12 +76,11 @@ LL | eq(foo::<u8>, bar::<u8> as fn(isize) -> isize);
|
= note: expected fn item `fn(_) -> _ {foo::<u8>}`
found fn pointer `fn(_) -> _`
= help: change the expected type to be function pointer `fn(isize) -> isize`
= help: if the expected type is due to type inference, cast the expected `fn` to a function pointer: `foo::<u8> as fn(isize) -> isize`
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

  1. When we expect a FnDef, and find a FnPtr, we should mention that the user can use an as to turn the def into a pointer. Since the span will be pointing at the ptr, not the def, this can't be a suggestion, but it's still worth noting.

= note: fn items are distinct from fn pointers
note: function defined here
--> $DIR/fn-item-type.rs:7:4
--> $DIR/fn-item-type.rs:11:4
|
LL | fn eq<T>(x: T, y: T) { }
LL | fn eq<T>(x: T, y: T) {}
| ^^ ----

error: aborting due to 5 previous errors
Loading