Skip to content

Improve presentation of closure signature mismatch from Fn trait goal #139515

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 2 commits into from
Apr 9, 2025
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
8 changes: 4 additions & 4 deletions compiler/rustc_errors/src/diagnostic.rs
Original file line number Diff line number Diff line change
Expand Up @@ -647,9 +647,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
#[rustc_lint_diagnostics]
pub fn note_expected_found(
&mut self,
expected_label: &dyn fmt::Display,
expected_label: &str,
expected: DiagStyledString,
found_label: &dyn fmt::Display,
found_label: &str,
found: DiagStyledString,
) -> &mut Self {
self.note_expected_found_extra(
Expand All @@ -665,9 +665,9 @@ impl<'a, G: EmissionGuarantee> Diag<'a, G> {
#[rustc_lint_diagnostics]
pub fn note_expected_found_extra(
&mut self,
expected_label: &dyn fmt::Display,
expected_label: &str,
expected: DiagStyledString,
found_label: &dyn fmt::Display,
found_label: &str,
found: DiagStyledString,
expected_extra: DiagStyledString,
found_extra: DiagStyledString,
Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_lint/src/lints.rs
Original file line number Diff line number Diff line change
Expand Up @@ -513,7 +513,7 @@ impl Subdiagnostic for BuiltinClashingExternSub<'_> {
expected_str.push(self.expected.fn_sig(self.tcx).to_string(), false);
let mut found_str = DiagStyledString::new();
found_str.push(self.found.fn_sig(self.tcx).to_string(), true);
diag.note_expected_found(&"", expected_str, &"", found_str);
diag.note_expected_found("", expected_str, "", found_str);
}
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -967,7 +967,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
format!("...so that the {}", sup_trace.cause.as_requirement_str()),
);

err.note_expected_found(&"", sup_expected, &"", sup_found);
err.note_expected_found("", sup_expected, "", sup_found);
return if sub_region.is_error() | sup_region.is_error() {
err.delay_as_bug()
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use core::ops::ControlFlow;
use std::borrow::Cow;
use std::path::PathBuf;

use rustc_abi::ExternAbi;
use rustc_ast::TraitObjectSyntax;
use rustc_data_structures::fx::FxHashMap;
use rustc_data_structures::unord::UnordSet;
Expand Down Expand Up @@ -2799,32 +2800,57 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}

// Note any argument mismatches
let given_ty = params.skip_binder();
let ty::Tuple(given) = *params.skip_binder().kind() else {
return;
};

let expected_ty = trait_pred.skip_binder().trait_ref.args.type_at(1);
if let ty::Tuple(given) = given_ty.kind()
&& let ty::Tuple(expected) = expected_ty.kind()
{
if expected.len() != given.len() {
// Note number of types that were expected and given
err.note(
format!(
"expected a closure taking {} argument{}, but one taking {} argument{} was given",
given.len(),
pluralize!(given.len()),
expected.len(),
pluralize!(expected.len()),
)
);
} else if !self.same_type_modulo_infer(given_ty, expected_ty) {
// Print type mismatch
let (expected_args, given_args) = self.cmp(given_ty, expected_ty);
err.note_expected_found(
&"a closure with arguments",
expected_args,
&"a closure with arguments",
given_args,
);
}
let ty::Tuple(expected) = *expected_ty.kind() else {
return;
};

if expected.len() != given.len() {
// Note number of types that were expected and given
err.note(format!(
"expected a closure taking {} argument{}, but one taking {} argument{} was given",
given.len(),
pluralize!(given.len()),
expected.len(),
pluralize!(expected.len()),
));
return;
}

let given_ty = Ty::new_fn_ptr(
self.tcx,
params.rebind(self.tcx.mk_fn_sig(
given,
self.tcx.types.unit,
false,
hir::Safety::Safe,
ExternAbi::Rust,
)),
);
let expected_ty = Ty::new_fn_ptr(
self.tcx,
trait_pred.rebind(self.tcx.mk_fn_sig(
expected,
self.tcx.types.unit,
false,
hir::Safety::Safe,
ExternAbi::Rust,
)),
);

if !self.same_type_modulo_infer(given_ty, expected_ty) {
// Print type mismatch
let (expected_args, given_args) = self.cmp(expected_ty, given_ty);
err.note_expected_found(
"a closure with signature",
expected_args,
"a closure with signature",
given_args,
);
}
}

Expand Down
2 changes: 1 addition & 1 deletion compiler/rustc_trait_selection/src/errors.rs
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ impl Subdiagnostic for RegionOriginNote<'_> {
label_or_note(span, fluent::trait_selection_subtype);
diag.arg("requirement", requirement);

diag.note_expected_found(&"", expected, &"", found);
diag.note_expected_found("", expected, "", found);
}
RegionOriginNote::WithRequirement { span, requirement, expected_found: None } => {
// FIXME: this really should be handled at some earlier stage. Our
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ LL | call(f, ());
| |
| required by a bound introduced by this call
|
= note: expected a closure with arguments `((),)`
found a closure with arguments `(<_ as ATC<'a>>::Type,)`
= note: expected a closure with signature `for<'a> fn(<_ as ATC<'a>>::Type)`
found a closure with signature `fn(())`
note: this is a known limitation of the trait solver that will be lifted in the future
--> $DIR/issue-62529-3.rs:25:14
|
Expand Down
4 changes: 2 additions & 2 deletions tests/ui/implied-bounds/issue-100690.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ LL | real_dispatch(f)
| |
| required by a bound introduced by this call
|
= note: expected a closure with arguments `(&mut UIView<'a, _>,)`
found a closure with arguments `(&mut UIView<'_, _>,)`
= note: expected a closure with signature `for<'a, 'b> fn(&'a mut UIView<'b, _>)`
found a closure with signature `fn(&mut UIView<'a, _>)`
note: required by a bound in `real_dispatch`
--> $DIR/issue-100690.rs:8:8
|
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,8 @@ LL | let _ = (-10..=10).find(|x: i32| x.signum() == 0);
| required by a bound introduced by this call
|
= help: the trait `for<'a> FnMut(&'a <std::ops::RangeInclusive<{integer}> as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:6:29: 6:37}`
= note: expected a closure with arguments `(i32,)`
found a closure with arguments `(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item,)`
= note: expected a closure with signature `for<'a> fn(&'a <std::ops::RangeInclusive<{integer}> as Iterator>::Item)`
Copy link
Contributor

@lcnr lcnr Apr 8, 2025

Choose a reason for hiding this comment

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

missing normalization with the new solver. idk where to best put it

Copy link
Member Author

Choose a reason for hiding this comment

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

Yeah, this will need to be fixed separately. Definitely orthogonal to this PR.

found a closure with signature `fn(i32)`
note: required by a bound in `find`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL

Expand All @@ -27,8 +27,8 @@ LL | let _ = (-10..=10).find(|x: &&&i32| x.signum() == 0);
| required by a bound introduced by this call
|
= help: the trait `for<'a> FnMut(&'a <std::ops::RangeInclusive<{integer}> as Iterator>::Item)` is not implemented for closure `{closure@$DIR/closure-arg-type-mismatch-issue-45727.rs:9:29: 9:40}`
= note: expected a closure with arguments `(&&&i32,)`
found a closure with arguments `(&<std::ops::RangeInclusive<{integer}> as Iterator>::Item,)`
= note: expected a closure with signature `for<'a> fn(&'a <std::ops::RangeInclusive<{integer}> as Iterator>::Item)`
found a closure with signature `fn(&&&i32)`
note: required by a bound in `find`
--> $SRC_DIR/core/src/iter/traits/iterator.rs:LL:COL

Expand Down
8 changes: 4 additions & 4 deletions tests/ui/trait-bounds/mismatch-fn-trait.stderr
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@ LL | take(f)
| |
| required by a bound introduced by this call
|
= note: expected a closure with arguments `(u32,)`
found a closure with arguments `(i32,)`
= note: expected a closure with signature `fn(i32)`
found a closure with signature `fn(u32)`
note: required by a bound in `take`
--> $DIR/mismatch-fn-trait.rs:1:18
|
Expand Down Expand Up @@ -68,8 +68,8 @@ LL | take(f)
| required by a bound introduced by this call
|
= note: `impl FnOnce(u32)` implements `FnOnce`, but it must implement `FnMut`, which is more general
= note: expected a closure with arguments `(u32,)`
found a closure with arguments `(i32,)`
= note: expected a closure with signature `fn(i32)`
found a closure with signature `fn(u32)`
Copy link
Contributor

Choose a reason for hiding this comment

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

why does this talk about closures for opaque types?

Copy link
Member Author

Choose a reason for hiding this comment

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

Because whoever implemented the diagnostic note originally hard-coded "closure" here rather than using the sort string of the type or whatever.

note: required by a bound in `take`
--> $DIR/mismatch-fn-trait.rs:1:18
|
Expand Down
Loading