Skip to content

Commit dac5b70

Browse files
committed
Tweak E0277 when predicate comes indirectly from ?
When a `?` operation requires an `Into` conversion with additional bounds (like having a concrete error but wanting to convert to a trait object), we handle it speficically and provide the same kind of information we give other `?` related errors. ``` error[E0277]: `?` couldn't convert the error: `E: std::error::Error` is not satisfied --> $DIR/bad-question-mark-on-trait-object.rs:5:13 | LL | fn foo() -> Result<(), Box<dyn std::error::Error>> { | -------------------------------------- required `E: std::error::Error` because of this LL | Ok(bar()?) | ^ the trait `std::error::Error` is not implemented for `E` | = note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait = note: required for `Box<dyn std::error::Error>` to implement `From<E>` ``` Avoid talking about `FromResidual` when other more relevant information is being given, particularly from `rust_on_unimplemented`.
1 parent 6eb4882 commit dac5b70

11 files changed

+103
-65
lines changed

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

+51-17
Original file line numberDiff line numberDiff line change
@@ -192,19 +192,38 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
192192

193193
let have_alt_message = message.is_some() || label.is_some();
194194
let is_try_conversion = self.is_try_conversion(span, main_trait_predicate.def_id());
195+
let is_question_mark = matches!(
196+
root_obligation.cause.code().peel_derives(),
197+
ObligationCauseCode::QuestionMark,
198+
) && !(
199+
self.tcx.is_diagnostic_item(sym::FromResidual, main_trait_predicate.def_id())
200+
|| self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::Try)
201+
);
195202
let is_unsize =
196203
self.tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Unsize);
204+
let question_mark_message = "the question mark operation (`?`) implicitly \
205+
performs a conversion on the error value \
206+
using the `From` trait";
197207
let (message, notes, append_const_msg) = if is_try_conversion {
208+
// We have a `-> Result<_, E1>` and `gives_E2()?`.
198209
(
199210
Some(format!(
200211
"`?` couldn't convert the error to `{}`",
201212
main_trait_predicate.skip_binder().self_ty(),
202213
)),
203-
vec![
204-
"the question mark operation (`?`) implicitly performs a \
205-
conversion on the error value using the `From` trait"
206-
.to_owned(),
207-
],
214+
vec![question_mark_message.to_owned()],
215+
Some(AppendConstMessage::Default),
216+
)
217+
} else if is_question_mark {
218+
// Similar to the case above, but in this case the conversion is for a
219+
// trait object: `-> Result<_, Box<dyn Error>` and `gives_E()?` when
220+
// `E: Error` isn't met.
221+
(
222+
Some(format!(
223+
"`?` couldn't convert the error: `{main_trait_predicate}` is \
224+
not satisfied",
225+
)),
226+
vec![question_mark_message.to_owned()],
208227
Some(AppendConstMessage::Default),
209228
)
210229
} else {
@@ -220,8 +239,10 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
220239
&mut long_ty_file,
221240
);
222241

223-
let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(main_trait_predicate.def_id(), LangItem::TransmuteTrait)
224-
{
242+
let (err_msg, safe_transmute_explanation) = if self.tcx.is_lang_item(
243+
main_trait_predicate.def_id(),
244+
LangItem::TransmuteTrait,
245+
) {
225246
// Recompute the safe transmute reason and use that for the error reporting
226247
match self.get_safe_transmute_error_and_reason(
227248
obligation.clone(),
@@ -249,18 +270,22 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
249270
*err.long_ty_path() = long_ty_file;
250271

251272
let mut suggested = false;
252-
if is_try_conversion {
273+
if is_try_conversion || is_question_mark {
253274
suggested = self.try_conversion_context(&obligation, main_trait_predicate, &mut err);
254275
}
255276

256-
if is_try_conversion && let Some(ret_span) = self.return_type_span(&obligation) {
257-
err.span_label(
258-
ret_span,
259-
format!(
260-
"expected `{}` because of this",
261-
main_trait_predicate.skip_binder().self_ty()
262-
),
263-
);
277+
if let Some(ret_span) = self.return_type_span(&obligation) {
278+
if is_try_conversion {
279+
err.span_label(
280+
ret_span,
281+
format!(
282+
"expected `{}` because of this",
283+
main_trait_predicate.skip_binder().self_ty()
284+
),
285+
);
286+
} else if is_question_mark {
287+
err.span_label(ret_span, format!("required `{main_trait_predicate}` because of this"));
288+
}
264289
}
265290

266291
if tcx.is_lang_item(leaf_trait_predicate.def_id(), LangItem::Tuple) {
@@ -302,10 +327,14 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
302327
// If it has a custom `#[rustc_on_unimplemented]`
303328
// error message, let's display it as the label!
304329
err.span_label(span, s);
305-
if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_)) {
330+
if !matches!(leaf_trait_predicate.skip_binder().self_ty().kind(), ty::Param(_))
306331
// When the self type is a type param We don't need to "the trait
307332
// `std::marker::Sized` is not implemented for `T`" as we will point
308333
// at the type param with a label to suggest constraining it.
334+
&& !self.tcx.is_diagnostic_item(sym::FromResidual, leaf_trait_predicate.def_id())
335+
// Don't say "the trait `FromResidual<Option<Infallible>>` is
336+
// not implemented for `Result<T, E>`".
337+
{
309338
err.help(explanation);
310339
}
311340
} else if let Some(custom_explanation) = safe_transmute_explanation {
@@ -2035,6 +2064,11 @@ impl<'a, 'tcx> TypeErrCtxt<'a, 'tcx> {
20352064
return false;
20362065
}
20372066
if let &[cand] = &candidates[..] {
2067+
if self.tcx.is_diagnostic_item(sym::FromResidual, cand.def_id)
2068+
&& !self.tcx.features().enabled(sym::try_trait_v2)
2069+
{
2070+
return false;
2071+
}
20382072
let (desc, mention_castable) =
20392073
match (cand.self_ty().kind(), trait_pred.self_ty().skip_binder().kind()) {
20402074
(ty::FnPtr(..), ty::FnDef(..)) => {

tests/ui/async-await/issue-84841.stderr

-2
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,6 @@ LL | | test()?;
1717
... |
1818
LL | | }
1919
| |_- this function should return `Result` or `Option` to accept `?`
20-
|
21-
= help: the trait `FromResidual<_>` is not implemented for `()`
2220

2321
error: aborting due to 2 previous errors
2422

tests/ui/async-await/try-on-option-in-async.stderr

-6
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ LL | async {
66
LL | let x: Option<u32> = None;
77
LL | x?;
88
| ^ cannot use the `?` operator in an async block that returns `{integer}`
9-
|
10-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `{integer}`
119

1210
error[E0277]: the `?` operator can only be used in an async closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
1311
--> $DIR/try-on-option-in-async.rs:16:10
@@ -20,8 +18,6 @@ LL | | x?;
2018
LL | | 22_u32
2119
LL | | };
2220
| |_____- this function should return `Result` or `Option` to accept `?`
23-
|
24-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32`
2521

2622
error[E0277]: the `?` operator can only be used in an async function that returns `Result` or `Option` (or another type that implements `FromResidual`)
2723
--> $DIR/try-on-option-in-async.rs:25:6
@@ -34,8 +30,6 @@ LL | | x?;
3430
LL | | 22
3531
LL | | }
3632
| |_- this function should return `Result` or `Option` to accept `?`
37-
|
38-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32`
3933

4034
error: aborting due to 3 previous errors
4135

tests/ui/return/return-from-residual-sugg-issue-125997.stderr

-6
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ LL | fn test1() {
66
LL | let mut _file = File::create("foo.txt")?;
77
| ^ cannot use the `?` operator in a function that returns `()`
88
|
9-
= help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
109
help: consider adding return type
1110
|
1211
LL ~ fn test1() -> Result<(), Box<dyn std::error::Error>> {
@@ -23,7 +22,6 @@ LL | fn test2() {
2322
LL | let mut _file = File::create("foo.txt")?;
2423
| ^ cannot use the `?` operator in a function that returns `()`
2524
|
26-
= help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
2725
help: consider adding return type
2826
|
2927
LL ~ fn test2() -> Result<(), Box<dyn std::error::Error>> {
@@ -41,7 +39,6 @@ LL | fn test4(&self) {
4139
LL | let mut _file = File::create("foo.txt")?;
4240
| ^ cannot use the `?` operator in a method that returns `()`
4341
|
44-
= help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
4542
help: consider adding return type
4643
|
4744
LL ~ fn test4(&self) -> Result<(), Box<dyn std::error::Error>> {
@@ -59,7 +56,6 @@ LL | fn test5(&self) {
5956
LL | let mut _file = File::create("foo.txt")?;
6057
| ^ cannot use the `?` operator in a method that returns `()`
6158
|
62-
= help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
6359
help: consider adding return type
6460
|
6561
LL ~ fn test5(&self) -> Result<(), Box<dyn std::error::Error>> {
@@ -78,7 +74,6 @@ LL | fn main() {
7874
LL | let mut _file = File::create("foo.txt")?;
7975
| ^ cannot use the `?` operator in a function that returns `()`
8076
|
81-
= help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
8277
help: consider adding return type
8378
|
8479
LL ~ fn main() -> Result<(), Box<dyn std::error::Error>> {
@@ -99,7 +94,6 @@ LL | let mut _file = File::create("foo.txt")?;
9994
LL | mac!();
10095
| ------ in this macro invocation
10196
|
102-
= help: the trait `FromResidual<Result<Infallible, std::io::Error>>` is not implemented for `()`
10397
= note: this error originates in the macro `mac` (in Nightly builds, run with -Z macro-backtrace for more info)
10498
help: consider adding return type
10599
|

tests/ui/try-trait/bad-interconversion.stderr

-12
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,6 @@ LL | fn option_to_result() -> Result<u64, String> {
2020
| -------------------------------------------- this function returns a `Result`
2121
LL | Some(3)?;
2222
| ^ use `.ok_or(...)?` to provide an error compatible with `Result<u64, String>`
23-
|
24-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u64, String>`
25-
= help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
2623

2724
error[E0277]: the `?` operator can only be used on `Result`s in a function that returns `Result`
2825
--> $DIR/bad-interconversion.rs:15:31
@@ -31,9 +28,6 @@ LL | fn control_flow_to_result() -> Result<u64, String> {
3128
| -------------------------------------------------- this function returns a `Result`
3229
LL | Ok(ControlFlow::Break(123)?)
3330
| ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Result<u64, String>`
34-
|
35-
= help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Result<u64, String>`
36-
= help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
3731

3832
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
3933
--> $DIR/bad-interconversion.rs:20:22
@@ -42,9 +36,6 @@ LL | fn result_to_option() -> Option<u16> {
4236
| ------------------------------------ this function returns an `Option`
4337
LL | Some(Err("hello")?)
4438
| ^ use `.ok()?` if you want to discard the `Result<Infallible, &str>` error information
45-
|
46-
= help: the trait `FromResidual<Result<Infallible, &str>>` is not implemented for `Option<u16>`
47-
= help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
4839

4940
error[E0277]: the `?` operator can only be used on `Option`s in a function that returns `Option`
5041
--> $DIR/bad-interconversion.rs:25:33
@@ -53,9 +44,6 @@ LL | fn control_flow_to_option() -> Option<u64> {
5344
| ------------------------------------------ this function returns an `Option`
5445
LL | Some(ControlFlow::Break(123)?)
5546
| ^ this `?` produces `ControlFlow<{integer}, Infallible>`, which is incompatible with `Option<u64>`
56-
|
57-
= help: the trait `FromResidual<ControlFlow<{integer}, Infallible>>` is not implemented for `Option<u64>`
58-
= help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
5947

6048
error[E0277]: the `?` operator can only be used on `ControlFlow`s in a function that returns `ControlFlow`
6149
--> $DIR/bad-interconversion.rs:30:39
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
struct E;
2+
struct X;
3+
4+
fn foo() -> Result<(), Box<dyn std::error::Error>> { //~ NOTE required `E: std::error::Error` because of this
5+
Ok(bar()?)
6+
//~^ ERROR `?` couldn't convert the error: `E: std::error::Error` is not satisfied
7+
//~| NOTE the trait `std::error::Error` is not implemented for `E`
8+
//~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
9+
//~| NOTE required for `Box<dyn std::error::Error>` to implement `From<E>`
10+
//~| NOTE in this expansion
11+
//~| NOTE in this expansion
12+
//~| NOTE in this expansion
13+
}
14+
fn bat() -> Result<(), X> { //~ NOTE expected `X` because of this
15+
Ok(bar()?)
16+
//~^ ERROR `?` couldn't convert the error to `X`
17+
//~| NOTE the trait `From<E>` is not implemented for `X`
18+
//~| NOTE this can't be annotated with `?` because it has type `Result<_, E>`
19+
//~| NOTE the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
20+
//~| NOTE in this expansion
21+
//~| NOTE in this expansion
22+
}
23+
fn bar() -> Result<(), E> {
24+
Err(E)
25+
}
26+
fn main() {}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
error[E0277]: `?` couldn't convert the error: `E: std::error::Error` is not satisfied
2+
--> $DIR/bad-question-mark-on-trait-object.rs:5:13
3+
|
4+
LL | fn foo() -> Result<(), Box<dyn std::error::Error>> {
5+
| -------------------------------------- required `E: std::error::Error` because of this
6+
LL | Ok(bar()?)
7+
| ^ the trait `std::error::Error` is not implemented for `E`
8+
|
9+
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
10+
= note: required for `Box<dyn std::error::Error>` to implement `From<E>`
11+
12+
error[E0277]: `?` couldn't convert the error to `X`
13+
--> $DIR/bad-question-mark-on-trait-object.rs:15:13
14+
|
15+
LL | fn bat() -> Result<(), X> {
16+
| ------------- expected `X` because of this
17+
LL | Ok(bar()?)
18+
| -----^ the trait `From<E>` is not implemented for `X`
19+
| |
20+
| this can't be annotated with `?` because it has type `Result<_, E>`
21+
|
22+
= note: the question mark operation (`?`) implicitly performs a conversion on the error value using the `From` trait
23+
24+
error: aborting due to 2 previous errors
25+
26+
For more information about this error, try `rustc --explain E0277`.

tests/ui/try-trait/option-to-result.stderr

-6
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ LL | fn test_result() -> Result<(),()> {
66
LL | let a:Option<()> = Some(());
77
LL | a?;
88
| ^ use `.ok_or(...)?` to provide an error compatible with `Result<(), ()>`
9-
|
10-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<(), ()>`
11-
= help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
129

1310
error[E0277]: the `?` operator can only be used on `Option`s, not `Result`s, in a function that returns `Option`
1411
--> $DIR/option-to-result.rs:11:6
@@ -18,9 +15,6 @@ LL | fn test_option() -> Option<i32>{
1815
LL | let a:Result<i32, i32> = Ok(5);
1916
LL | a?;
2017
| ^ use `.ok()?` if you want to discard the `Result<Infallible, i32>` error information
21-
|
22-
= help: the trait `FromResidual<Result<Infallible, i32>>` is not implemented for `Option<i32>`
23-
= help: the trait `FromResidual<Option<Infallible>>` is implemented for `Option<T>`
2418

2519
error: aborting due to 2 previous errors
2620

tests/ui/try-trait/try-on-option-diagnostics.stderr

-8
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,6 @@ LL | fn a_function() -> u32 {
66
LL | let x: Option<u32> = None;
77
LL | x?;
88
| ^ cannot use the `?` operator in a function that returns `u32`
9-
|
10-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32`
119

1210
error[E0277]: the `?` operator can only be used in a closure that returns `Result` or `Option` (or another type that implements `FromResidual`)
1311
--> $DIR/try-on-option-diagnostics.rs:14:10
@@ -17,8 +15,6 @@ LL | let a_closure = || {
1715
LL | let x: Option<u32> = None;
1816
LL | x?;
1917
| ^ cannot use the `?` operator in a closure that returns `{integer}`
20-
|
21-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `{integer}`
2218

2319
error[E0277]: the `?` operator can only be used in a method that returns `Result` or `Option` (or another type that implements `FromResidual`)
2420
--> $DIR/try-on-option-diagnostics.rs:26:14
@@ -28,8 +24,6 @@ LL | fn a_method() {
2824
LL | let x: Option<u32> = None;
2925
LL | x?;
3026
| ^ cannot use the `?` operator in a method that returns `()`
31-
|
32-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `()`
3327

3428
error[E0277]: the `?` operator can only be used in a trait method that returns `Result` or `Option` (or another type that implements `FromResidual`)
3529
--> $DIR/try-on-option-diagnostics.rs:39:14
@@ -39,8 +33,6 @@ LL | fn a_trait_method() {
3933
LL | let x: Option<u32> = None;
4034
LL | x?;
4135
| ^ cannot use the `?` operator in a trait method that returns `()`
42-
|
43-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `()`
4436

4537
error: aborting due to 4 previous errors
4638

tests/ui/try-trait/try-on-option.stderr

-5
Original file line numberDiff line numberDiff line change
@@ -6,9 +6,6 @@ LL | fn foo() -> Result<u32, ()> {
66
LL | let x: Option<u32> = None;
77
LL | x?;
88
| ^ use `.ok_or(...)?` to provide an error compatible with `Result<u32, ()>`
9-
|
10-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `Result<u32, ()>`
11-
= help: the trait `FromResidual<Result<Infallible, E>>` is implemented for `Result<T, F>`
129

1310
error[E0277]: the `?` operator can only be used in a function that returns `Result` or `Option` (or another type that implements `FromResidual`)
1411
--> $DIR/try-on-option.rs:11:6
@@ -18,8 +15,6 @@ LL | fn bar() -> u32 {
1815
LL | let x: Option<u32> = None;
1916
LL | x?;
2017
| ^ cannot use the `?` operator in a function that returns `u32`
21-
|
22-
= help: the trait `FromResidual<Option<Infallible>>` is not implemented for `u32`
2318

2419
error: aborting due to 2 previous errors
2520

0 commit comments

Comments
 (0)