Skip to content

Commit 497c9a2

Browse files
committed
Auto merge of #80517 - wabain:issue-77880-infer-error-try-conversion-msg, r=davidtwco
Enhance type inference errors involving the `?` operator This patch adds a special-cased note on type inference errors when the error span points to a `?` return. It also makes the primary label for such errors "cannot infer type of `?` error" in cases where before we would have only said "cannot infer type". One beneficiary of this change is async blocks, where we can't explicitly annotate the return type and so may not generate any other help (#77880); this lets us at least print the error type we're converting from and anything we know about the type we can't fully infer. More generally, it signposts that an implicit conversion is happening that may have impeded type inference the user was expecting. We already do something similar for [mismatched type errors](https://github.com/rust-lang/rust/blob/2987785df3d46d5ff144a5c67fbb8f5cca798d78/src/test/ui/try-block/try-block-bad-type.stderr#L7). The check for a relevant `?` operator is built into the existing HIR traversal which looks for places that could be annotated to resolve the error. That means we could identify `?` uses anywhere in the function that output the type we can't infer, but this patch just sticks to adding the note if the primary span given for the error has the operator; if there are other expressions where the type occurs and one of them is selected for the error instead, it's more likely that the `?` operator's implicit conversion isn't the sole cause of the inference failure and that adding an additional diagnostic would just be noise. I added a ui test for one such case. The data about the `?` conversion is passed around in a `UseDiagnostic` enum that in theory could be used to add more of this kind of note in the future. It was also just easier to pass around than something with a more specific name. There are some follow-up refactoring commits for the code that generates the error label, which was already pretty involved and made a bit more complicated by this change.
2 parents fc9944f + d46c3e3 commit 497c9a2

10 files changed

+292
-168
lines changed

compiler/rustc_infer/src/infer/error_reporting/mod.rs

+9-1
Original file line numberDiff line numberDiff line change
@@ -69,7 +69,7 @@ use rustc_middle::ty::{
6969
subst::{Subst, SubstsRef},
7070
Region, Ty, TyCtxt, TypeFoldable,
7171
};
72-
use rustc_span::{BytePos, DesugaringKind, Pos, Span};
72+
use rustc_span::{sym, BytePos, DesugaringKind, Pos, Span};
7373
use rustc_target::spec::abi;
7474
use std::ops::ControlFlow;
7575
use std::{cmp, fmt};
@@ -2282,6 +2282,14 @@ impl<'a, 'tcx> InferCtxt<'a, 'tcx> {
22822282
self.note_region_origin(&mut err, &sub_origin);
22832283
err.emit();
22842284
}
2285+
2286+
/// Determine whether an error associated with the given span and definition
2287+
/// should be treated as being caused by the implicit `From` conversion
2288+
/// within `?` desugaring.
2289+
pub fn is_try_conversion(&self, span: Span, trait_def_id: DefId) -> bool {
2290+
span.is_desugaring(DesugaringKind::QuestionMark)
2291+
&& self.tcx.is_diagnostic_item(sym::from_trait, trait_def_id)
2292+
}
22852293
}
22862294

22872295
impl<'a, 'tcx> InferCtxt<'a, 'tcx> {

compiler/rustc_infer/src/infer/error_reporting/need_type_info.rs

+212-153
Large diffs are not rendered by default.

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

+3-11
Original file line numberDiff line numberDiff line change
@@ -280,18 +280,10 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
280280
let OnUnimplementedNote { message, label, note, enclosing_scope } =
281281
self.on_unimplemented_note(trait_ref, obligation);
282282
let have_alt_message = message.is_some() || label.is_some();
283-
let is_try = self
284-
.tcx
285-
.sess
286-
.source_map()
287-
.span_to_snippet(span)
288-
.map(|s| &s == "?")
289-
.unwrap_or(false);
290-
let is_from = self.tcx.get_diagnostic_item(sym::from_trait)
291-
== Some(trait_ref.def_id());
283+
let is_try_conversion = self.is_try_conversion(span, trait_ref.def_id());
292284
let is_unsize =
293285
{ Some(trait_ref.def_id()) == self.tcx.lang_items().unsize_trait() };
294-
let (message, note) = if is_try && is_from {
286+
let (message, note) = if is_try_conversion {
295287
(
296288
Some(format!(
297289
"`?` couldn't convert the error to `{}`",
@@ -319,7 +311,7 @@ impl<'a, 'tcx> InferCtxtExt<'tcx> for InferCtxt<'a, 'tcx> {
319311
))
320312
);
321313

322-
if is_try && is_from {
314+
if is_try_conversion {
323315
let none_error = self
324316
.tcx
325317
.get_diagnostic_item(sym::none_error)

src/test/ui/inference/cannot-infer-async-enabled-impl-trait-bindings.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,9 @@ error[E0282]: type annotations needed for `impl Future`
1313
LL | let fut = async {
1414
| --- consider giving `fut` the explicit type `impl Future`, with the type parameters specified
1515
LL | make_unit()?;
16-
| ^ cannot infer type
16+
| ^ cannot infer type of error for `?` operator
17+
|
18+
= note: `?` implicitly converts the error value into a type implementing `From<std::io::Error>`
1719

1820
error: aborting due to previous error; 1 warning emitted
1921

src/test/ui/inference/cannot-infer-async.stderr

+3-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@ error[E0282]: type annotations needed
44
LL | let fut = async {
55
| --- consider giving `fut` a type
66
LL | make_unit()?;
7-
| ^ cannot infer type
7+
| ^ cannot infer type of error for `?` operator
8+
|
9+
= note: `?` implicitly converts the error value into a type implementing `From<std::io::Error>`
810

911
error: aborting due to previous error
1012

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
fn main() {
2+
// Below we call the closure with its own return as the argument, unifying
3+
// its inferred input and return types. We want to make sure that the generated
4+
// error handles this gracefully, and in particular doesn't generate an extra
5+
// note about the `?` operator in the closure body, which isn't relevant to
6+
// the inference.
7+
let x = |r| {
8+
//~^ ERROR type annotations needed
9+
let v = r?;
10+
Ok(v)
11+
};
12+
13+
let _ = x(x(Ok(())));
14+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
error[E0282]: type annotations needed for `std::result::Result<(), E>`
2+
--> $DIR/cannot-infer-closure-circular.rs:7:14
3+
|
4+
LL | let x = |r| {
5+
| ^ consider giving this closure parameter the explicit type `std::result::Result<(), E>`, with the type parameters specified
6+
7+
error: aborting due to previous error
8+
9+
For more information about this error, try `rustc --explain E0282`.

src/test/ui/inference/cannot-infer-closure.stderr

+2-1
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,9 @@ error[E0282]: type annotations needed for the closure `fn((), ()) -> std::result
22
--> $DIR/cannot-infer-closure.rs:3:15
33
|
44
LL | Err(a)?;
5-
| ^ cannot infer type
5+
| ^ cannot infer type of error for `?` operator
66
|
7+
= note: `?` implicitly converts the error value into a type implementing `From<()>`
78
help: give this closure an explicit return type without `_` placeholders
89
|
910
LL | let x = |a: (), b: ()| -> std::result::Result<(), _> {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
struct QualifiedError<E>(E);
2+
3+
impl<E, T> From<E> for QualifiedError<T>
4+
where
5+
E: std::error::Error,
6+
T: From<E>,
7+
{
8+
fn from(e: E) -> QualifiedError<T> {
9+
QualifiedError(e.into())
10+
}
11+
}
12+
13+
fn infallible() -> Result<(), std::convert::Infallible> {
14+
Ok(())
15+
}
16+
17+
fn main() {
18+
let x = || -> Result<_, QualifiedError<_>> {
19+
infallible()?; //~ERROR type annotations needed
20+
Ok(())
21+
};
22+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
error[E0282]: type annotations needed for the closure `fn() -> std::result::Result<(), QualifiedError<_>>`
2+
--> $DIR/cannot-infer-partial-try-return.rs:19:9
3+
|
4+
LL | infallible()?;
5+
| ^^^^^^^^^^^^^ cannot infer type of error for `?` operator
6+
|
7+
= note: `?` implicitly converts the error value into `QualifiedError<_>` using its implementation of `From<Infallible>`
8+
help: give this closure an explicit return type without `_` placeholders
9+
|
10+
LL | let x = || -> std::result::Result<(), QualifiedError<_>> {
11+
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
12+
13+
error: aborting due to previous error
14+
15+
For more information about this error, try `rustc --explain E0282`.

0 commit comments

Comments
 (0)