Skip to content

Commit

Permalink
Rollup merge of rust-lang#101360 - compiler-errors:multiple-closure-b…
Browse files Browse the repository at this point in the history
…ounds, r=petrochenkov

Point out incompatible closure bounds

Fixes rust-lang#100295
  • Loading branch information
Dylan-DPC committed Oct 10, 2022
2 parents 691aeaa + 6934853 commit 8921e6b
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 0 deletions.
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`.

0 comments on commit 8921e6b

Please sign in to comment.