Skip to content

Commit d8d01e3

Browse files
authored
Rollup merge of #101360 - compiler-errors:multiple-closure-bounds, r=petrochenkov
Point out incompatible closure bounds Fixes #100295
2 parents 0265a3e + 6934853 commit d8d01e3

File tree

4 files changed

+107
-0
lines changed

4 files changed

+107
-0
lines changed

compiler/rustc_trait_selection/src/traits/error_reporting/mod.rs

+1
Original file line numberDiff line numberDiff line change
@@ -1255,6 +1255,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
12551255
found_span,
12561256
found_trait_ref,
12571257
expected_trait_ref,
1258+
obligation.cause.code(),
12581259
)
12591260
} else {
12601261
let (closure_span, found) = found_did

compiler/rustc_trait_selection/src/traits/error_reporting/suggestions.rs

+67
Original file line numberDiff line numberDiff line change
@@ -254,8 +254,15 @@ pub trait TypeErrCtxtExt<'tcx> {
254254
found_span: Option<Span>,
255255
found: ty::PolyTraitRef<'tcx>,
256256
expected: ty::PolyTraitRef<'tcx>,
257+
cause: &ObligationCauseCode<'tcx>,
257258
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed>;
258259

260+
fn note_conflicting_closure_bounds(
261+
&self,
262+
cause: &ObligationCauseCode<'tcx>,
263+
err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
264+
);
265+
259266
fn suggest_fully_qualified_path(
260267
&self,
261268
err: &mut Diagnostic,
@@ -1584,6 +1591,7 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
15841591
found_span: Option<Span>,
15851592
found: ty::PolyTraitRef<'tcx>,
15861593
expected: ty::PolyTraitRef<'tcx>,
1594+
cause: &ObligationCauseCode<'tcx>,
15871595
) -> DiagnosticBuilder<'tcx, ErrorGuaranteed> {
15881596
pub(crate) fn build_fn_sig_ty<'tcx>(
15891597
infcx: &InferCtxt<'tcx>,
@@ -1645,9 +1653,68 @@ impl<'tcx> TypeErrCtxtExt<'tcx> for TypeErrCtxt<'_, 'tcx> {
16451653
let signature_kind = format!("{argument_kind} signature");
16461654
err.note_expected_found(&signature_kind, expected_str, &signature_kind, found_str);
16471655

1656+
self.note_conflicting_closure_bounds(cause, &mut err);
1657+
16481658
err
16491659
}
16501660

1661+
// Add a note if there are two `Fn`-family bounds that have conflicting argument
1662+
// requirements, which will always cause a closure to have a type error.
1663+
fn note_conflicting_closure_bounds(
1664+
&self,
1665+
cause: &ObligationCauseCode<'tcx>,
1666+
err: &mut DiagnosticBuilder<'tcx, ErrorGuaranteed>,
1667+
) {
1668+
// First, look for an `ExprBindingObligation`, which means we can get
1669+
// the unsubstituted predicate list of the called function. And check
1670+
// that the predicate that we failed to satisfy is a `Fn`-like trait.
1671+
if let ObligationCauseCode::ExprBindingObligation(def_id, _, _, idx) = cause
1672+
&& let predicates = self.tcx.predicates_of(def_id).instantiate_identity(self.tcx)
1673+
&& let Some(pred) = predicates.predicates.get(*idx)
1674+
&& let ty::PredicateKind::Trait(trait_pred) = pred.kind().skip_binder()
1675+
&& ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id()).is_some()
1676+
{
1677+
let expected_self =
1678+
self.tcx.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.self_ty()));
1679+
let expected_substs = self
1680+
.tcx
1681+
.anonymize_late_bound_regions(pred.kind().rebind(trait_pred.trait_ref.substs));
1682+
1683+
// Find another predicate whose self-type is equal to the expected self type,
1684+
// but whose substs don't match.
1685+
let other_pred = std::iter::zip(&predicates.predicates, &predicates.spans)
1686+
.enumerate()
1687+
.find(|(other_idx, (pred, _))| match pred.kind().skip_binder() {
1688+
ty::PredicateKind::Trait(trait_pred)
1689+
if ty::ClosureKind::from_def_id(self.tcx, trait_pred.def_id())
1690+
.is_some()
1691+
&& other_idx != idx
1692+
// Make sure that the self type matches
1693+
// (i.e. constraining this closure)
1694+
&& expected_self
1695+
== self.tcx.anonymize_late_bound_regions(
1696+
pred.kind().rebind(trait_pred.self_ty()),
1697+
)
1698+
// But the substs don't match (i.e. incompatible args)
1699+
&& expected_substs
1700+
!= self.tcx.anonymize_late_bound_regions(
1701+
pred.kind().rebind(trait_pred.trait_ref.substs),
1702+
) =>
1703+
{
1704+
true
1705+
}
1706+
_ => false,
1707+
});
1708+
// If we found one, then it's very likely the cause of the error.
1709+
if let Some((_, (_, other_pred_span))) = other_pred {
1710+
err.span_note(
1711+
*other_pred_span,
1712+
"closure inferred to have a different signature due to this bound",
1713+
);
1714+
}
1715+
}
1716+
}
1717+
16511718
fn suggest_fully_qualified_path(
16521719
&self,
16531720
err: &mut Diagnostic,
+15
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
2+
//~^ NOTE required by a bound in `foo`
3+
//~| NOTE required by this bound in `foo`
4+
//~| NOTE closure inferred to have a different signature due to this bound
5+
todo!();
6+
}
7+
8+
fn main() {
9+
let v = true;
10+
foo(move |x| v);
11+
//~^ ERROR type mismatch in closure arguments
12+
//~| NOTE expected closure signature
13+
//~| NOTE expected due to this
14+
//~| NOTE found signature defined here
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
error[E0631]: type mismatch in closure arguments
2+
--> $DIR/multiple-fn-bounds.rs:10:5
3+
|
4+
LL | foo(move |x| v);
5+
| ^^^ -------- found signature defined here
6+
| |
7+
| expected due to this
8+
|
9+
= note: expected closure signature `fn(char) -> _`
10+
found closure signature `for<'a> fn(&'a char) -> _`
11+
note: closure inferred to have a different signature due to this bound
12+
--> $DIR/multiple-fn-bounds.rs:1:11
13+
|
14+
LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
15+
| ^^^^^^^^^^^^^^^^^
16+
note: required by a bound in `foo`
17+
--> $DIR/multiple-fn-bounds.rs:1:31
18+
|
19+
LL | fn foo<F: Fn(&char) -> bool + Fn(char) -> bool>(f: F) {
20+
| ^^^^^^^^^^^^^^^^ required by this bound in `foo`
21+
22+
error: aborting due to previous error
23+
24+
For more information about this error, try `rustc --explain E0631`.

0 commit comments

Comments
 (0)