When encountering unexpected closure return type, point at return type/expression

```
error[E0271]: expected `{closure@fallback-closure-wrap.rs:18:40}` to be a closure that returns `()`, but it returns `!`
  --> $DIR/fallback-closure-wrap.rs:19:9
   |
LL |     let error = Closure::wrap(Box::new(move || {
   |                                        -------
LL |         panic!("Can't connect to server.");
   |         ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ expected `()`, found `!`
   |
   = note: expected unit type `()`
                   found type `!`
   = note: required for the cast from `Box<{closure@$DIR/fallback-closure-wrap.rs:18:40: 18:47}>` to `Box<dyn FnMut()>`
```

```
error[E0271]: expected `{closure@dont-ice-for-type-mismatch-in-closure-in-async.rs:6:10}` to be a closure that returns `bool`, but it returns `Option<()>`
  --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:6:16
   |
LL |     call(|| -> Option<()> {
   |     ---- ------^^^^^^^^^^
   |     |          |
   |     |          expected `bool`, found `Option<()>`
   |     required by a bound introduced by this call
   |
   = note: expected type `bool`
              found enum `Option<()>`
note: required by a bound in `call`
  --> $DIR/dont-ice-for-type-mismatch-in-closure-in-async.rs:3:25
   |
LL | fn call(_: impl Fn() -> bool) {}
   |                         ^^^^ required by this bound in `call`
```

```
error[E0271]: expected `{closure@f670.rs:28:13}` to be a closure that returns `Result<(), _>`, but it returns `!`
    --> f670.rs:28:20
     |
28   |     let c = |e| -> ! {
     |             -------^
     |                    |
     |                    expected `Result<(), _>`, found `!`
...
32   |     f().or_else(c);
     |         ------- required by a bound introduced by this call
-Ztrack-diagnostics: created at compiler/rustc_trait_selection/src/error_reporting/traits/fulfillment_errors.rs:1433:28
     |
     = note: expected enum `Result<(), _>`
                found type `!`
note: required by a bound in `Result::<T, E>::or_else`
    --> /home/gh-estebank/rust/library/core/src/result.rs:1406:39
     |
1406 |     pub fn or_else<F, O: FnOnce(E) -> Result<T, F>>(self, op: O) -> Result<T, F> {
     |                                       ^^^^^^^^^^^^ required by this bound in `Result::<T, E>::or_else`
```
This commit is contained in:
Esteban Küber 2024-10-25 21:27:13 +00:00
parent a6434ef9c0
commit d3a148fe07
11 changed files with 114 additions and 65 deletions

View file

@ -1386,6 +1386,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
#[instrument(level = "debug", skip(self, diag, secondary_span, prefer_label))]
pub fn note_type_err(
&self,
span: Span,
diag: &mut Diag<'_>,
cause: &ObligationCause<'tcx>,
secondary_span: Option<(Span, Cow<'static, str>, bool)>,
@ -1393,8 +1394,6 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
terr: TypeError<'tcx>,
prefer_label: bool,
) {
let span = cause.span;
// For some types of errors, expected-found does not make
// sense, so just ignore the values we were given.
if let TypeError::CyclicTy(_) = terr {
@ -2053,6 +2052,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
let mut diag = self.dcx().create_err(failure_code);
self.note_type_err(
span,
&mut diag,
&trace.cause,
None,

View file

@ -702,6 +702,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
);
self.note_type_err(
span,
&mut diag,
&obligation.cause,
None,
@ -931,14 +932,9 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
}
}
let hir_id = self.tcx.local_def_id_to_hir_id(obligation.cause.body_id);
let body_id = match self.tcx.hir_node(hir_id) {
hir::Node::Item(hir::Item {
kind: hir::ItemKind::Fn { body: body_id, .. }, ..
}) => body_id,
_ => return false,
};
let ControlFlow::Break(expr) = (FindMethodSubexprOfTry { search_span: span })
.visit_body(self.tcx.hir().body(*body_id))
let Some(body_id) = self.tcx.hir_node(hir_id).body_id() else { return false };
let ControlFlow::Break(expr) =
(FindMethodSubexprOfTry { search_span: span }).visit_body(self.tcx.hir().body(body_id))
else {
return false;
};
@ -1385,9 +1381,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
_ => (None, error.err),
};
let msg = values
let (msg, span, closure_span) = values
.and_then(|(predicate, normalized_term, expected_term)| {
self.maybe_detailed_projection_msg(predicate, normalized_term, expected_term)
self.maybe_detailed_projection_msg(
obligation.cause.span,
predicate,
normalized_term,
expected_term,
)
})
.unwrap_or_else(|| {
let mut cx = FmtPrinter::new_with_limit(
@ -1395,12 +1396,35 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
Namespace::TypeNS,
rustc_session::Limit(10),
);
with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", {
self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
cx.into_buffer()
}))
(
with_forced_trimmed_paths!(format!("type mismatch resolving `{}`", {
self.resolve_vars_if_possible(predicate).print(&mut cx).unwrap();
cx.into_buffer()
})),
obligation.cause.span,
None,
)
});
let mut diag = struct_span_code_err!(self.dcx(), obligation.cause.span, E0271, "{msg}");
let mut diag = struct_span_code_err!(self.dcx(), span, E0271, "{msg}");
if let Some(span) = closure_span {
// Mark the closure decl so that it is seen even if we are pointing at the return
// type or expression.
//
// error[E0271]: expected `{closure@foo.rs:41:16}` to be a closure that returns
// `Unit3`, but it returns `Unit4`
// --> $DIR/foo.rs:43:17
// |
// LL | let v = Unit2.m(
// | - required by a bound introduced by this call
// ...
// LL | f: |x| {
// | --- /* this span */
// LL | drop(x);
// LL | Unit4
// | ^^^^^ expected `Unit3`, found `Unit4`
// |
diag.span_label(span, "");
}
let secondary_span = (|| {
let ty::PredicateKind::Clause(ty::ClauseKind::Projection(proj)) =
@ -1460,6 +1484,7 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
})();
self.note_type_err(
span,
&mut diag,
&obligation.cause,
secondary_span,
@ -1479,34 +1504,63 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
fn maybe_detailed_projection_msg(
&self,
mut span: Span,
projection_term: ty::AliasTerm<'tcx>,
normalized_ty: ty::Term<'tcx>,
expected_ty: ty::Term<'tcx>,
) -> Option<String> {
) -> Option<(String, Span, Option<Span>)> {
let trait_def_id = projection_term.trait_def_id(self.tcx);
let self_ty = projection_term.self_ty();
with_forced_trimmed_paths! {
if self.tcx.is_lang_item(projection_term.def_id, LangItem::FnOnceOutput) {
let fn_kind = self_ty.prefix_string(self.tcx);
let (span, closure_span) = if let ty::Closure(def_id, _) = self_ty.kind() {
let def_span = self.tcx.def_span(def_id);
let node = self.tcx.hir_node_by_def_id(def_id.as_local().unwrap());
if let Some(fn_decl) = node.fn_decl() {
span = match fn_decl.output {
hir::FnRetTy::Return(ty) => ty.span,
hir::FnRetTy::DefaultReturn(_) => {
let body = self.tcx.hir().body(node.body_id().unwrap());
match body.value.kind {
hir::ExprKind::Block(
hir::Block { expr: Some(expr), .. },
_,
) => expr.span,
hir::ExprKind::Block(
hir::Block {
expr: None, stmts: [.., last], ..
},
_,
) => last.span,
_ => body.value.span,
}
}
};
}
(span, Some(def_span))
} else {
(span, None)
};
let item = match self_ty.kind() {
ty::FnDef(def, _) => self.tcx.item_name(*def).to_string(),
_ => self_ty.to_string(),
};
Some(format!(
Some((format!(
"expected `{item}` to be a {fn_kind} that returns `{expected_ty}`, but it \
returns `{normalized_ty}`",
))
), span, closure_span))
} else if self.tcx.is_lang_item(trait_def_id, LangItem::Future) {
Some(format!(
Some((format!(
"expected `{self_ty}` to be a future that resolves to `{expected_ty}`, but it \
resolves to `{normalized_ty}`"
))
), span, None))
} else if Some(trait_def_id) == self.tcx.get_diagnostic_item(sym::Iterator) {
Some(format!(
Some((format!(
"expected `{self_ty}` to be an iterator that yields `{expected_ty}`, but it \
yields `{normalized_ty}`"
))
), span, None))
} else {
None
}