Skip to content

Point out incompatible closure bounds #101360

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
Oct 11, 2022
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
Original file line number Diff line number Diff line change
Expand Up @@ -1255,6 +1255,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
found_span,
found_trait_ref,
expected_trait_ref,
obligation.cause.code(),
)
} else {
let (closure_span, found) = found_did
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -254,8 +254,15 @@ pub trait TypeErrCtxtExt<'tcx> {
found_span: Option<Span>,
found: ty::PolyTraitRef<'tcx>,
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;

fn note_conflicting_closure_bounds(
&self,
cause: &ObligationCauseCode<'tcx>,
err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
);

fn suggest_fully_qualified_path(
&self,
err: &mut Diagnostic,
Expand Down Expand Up @@ -1584,6 +1591,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
found_span: Option<Span>,
found: ty::PolyTraitRef<'tcx>,
expected: ty::PolyTraitRef<'tcx>,
cause: &ObligationCauseCode<'tcx>,
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
pub(crate) fn build_fn_sig_ty<'tcx>(
infcx: &InferCtxt<'tcx>,
Expand Down Expand Up @@ -1645,9 +1653,68 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
let signature_kind = format!("{argument_kind} signature");
err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str);

self.note_conflicting_closure_bounds(cause, &mut err);

err
}

// Add a note if there are two `Fn`-family bounds that have conflicting argument
// requirements, which will always cause a closure to have a type error.
fn note_conflicting_closure_bounds(
&self,
cause: &ObligationCauseCode<'tcx>,
err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
) {
// First, look for an `ExprBindingObligation`, which means we can get
// the unsubstituted predicate list of the called function. And check
// that the predicate that we failed to satisfy is a `Fn`-like trait.
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = cause
&& let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
&& let Some(pred) = predicates.predicates.get(*idx)
&& let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder()
&& ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id()).is_some()
{
let expected_self =
self.tcx.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.self_ty()));
let expected_substs = self
.tcx
.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.trait_ref.substs));

// Find another predicate whose self-type is equal to the expected self type,
// but whose substs don't match.
let other_pred = std::iter::zip(&predicates.predicates, &predicates.spans)
.enumerate()
.find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
ty::PredicateKind::Trait(trait_pred)
if ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id())
.is_some()
&& other_idx != idx
// Make sure that the self type matches
// (i.e. constraining this closure)
&& expected_self
== self.tcx.anonymize_late_bound_regions(
pred.kind().rebind(trait_pred.self_ty()),
)
// But the substs don't match (i.e. incompatible args)
&& expected_substs
!= self.tcx.anonymize_late_bound_regions(
pred.kind().rebind(trait_pred.trait_ref.substs),
) =>
{
true
}
_ => false,
});
// If we found one, then it's very likely the cause of the error.
if let Some((_, (_, other_pred_span))) = other_pred {
err.span_note(
*other_pred_span,
"closure inferred to have a different signature due to this bound",
);
}
}
}

fn suggest_fully_qualified_path(
&self,
err: &mut Diagnostic,
Expand Down
15 changes: 15 additions & 0 deletions src/test/ui/closures/multiple-fn-bounds.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
//~^ NOTE required by a bound in `foo`
//~| NOTE required by this bound in `foo`
//~| NOTE closure inferred to have a different signature due to this bound
todo!();
}

fn main() {
let v = true;
foo(move |x| v);
//~^ ERROR type mismatch in closure arguments
//~| NOTE expected closure signature
//~| NOTE expected due to this
//~| NOTE found signature defined here
}
24 changes: 24 additions & 0 deletions src/test/ui/closures/multiple-fn-bounds.stderr
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
error[E0631]: type mismatch in closure arguments
--> $DIR/multiple-fn-bounds.rs:10:5
|
LL | foo(move |x| v);
| ^^^ -------- found signature defined here
| |
| expected due to this
|
= note: expected closure signature `fn(char) -> _`
found closure signature `for<'a> fn(&'a char) -> _`
note: closure inferred to have a different signature due to this bound
--> $DIR/multiple-fn-bounds.rs:1:11
|
LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
| ^^^^^^^^^^^^^^^^^
note: required by a bound in `foo`
--> $DIR/multiple-fn-bounds.rs:1:31
|
LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
| ^^^^^^^^^^^^^^^^ required by this bound in `foo`

error: aborting due to previous error

For more information about this error, try `rustc --explain E0631`.