Skip to content
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

Add help message about function pointers #105552

Merged
merged 1 commit into from
Jan 25, 2023
Merged
Show file tree
Hide file tree
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
Expand Up @@ -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,
Expand Down
1 change: 0 additions & 1 deletion compiler/rustc_hir_typeck/src/demand.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
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
Expand Up @@ -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")]
Expand Down
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
Expand Up @@ -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);
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved
}
}

Expand Down Expand Up @@ -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()
}
Expand Down
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
Expand Up @@ -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;
Expand Down Expand Up @@ -351,6 +351,82 @@ impl<'tcx> TypeErrCtxt<'_, 'tcx> {
}
}

pub(super) fn suggest_function_pointers(
compiler-errors marked this conversation as resolved.
Show resolved Hide resolved
&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()) {
mattjperez marked this conversation as resolved.
Show resolved Hide resolved
(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())
Expand Down
1 change: 1 addition & 0 deletions tests/ui/fn/fn-compare-mismatch.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
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
Expand All @@ -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`
Expand All @@ -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`
Expand All @@ -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`
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading